From 6f5adc056848d2ffd0cd13d18fe8397406ed03f0 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 5 Aug 2024 00:27:09 +0200 Subject: [PATCH] drm: s upport explicit sync with multi-gpu destinations will break o n mgpu nvidia before 560 driver --- src/backend/drm/DRM.cpp | 14 ++++- src/backend/drm/Renderer.cpp | 114 ++++++++++++++++++++++++++++++++--- src/backend/drm/Renderer.hpp | 25 ++++++-- 3 files changed, 137 insertions(+), 16 deletions(-) diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index cfbef0d..80ca92a 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -1474,12 +1474,20 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { return false; } - auto NEWAQBUF = mgpu.swapchain->next(nullptr); - if (!backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF)) { + auto NEWAQBUF = mgpu.swapchain->next(nullptr); + auto blitResult = backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF, + (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE) ? STATE.explicitInFence : -1); + if (!blitResult.success) { backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed"); return false; } + // replace the explicit in fence if the blitting backend returned one, otherwise discard old. Passed fence from the client is wrong. + if (blitResult.syncFD.has_value()) + state->setExplicitInFence(blitResult.syncFD.value()); + else + state->setExplicitInFence(-1); + drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present } else drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present @@ -1625,7 +1633,7 @@ bool Aquamarine::CDRMOutput::setCursor(SP buffer, const Vector2D& hotsp } auto NEWAQBUF = mgpu.cursorSwapchain->next(nullptr); - if (!backend->mgpu.renderer->blit(buffer, NEWAQBUF)) { + if (!backend->mgpu.renderer->blit(buffer, NEWAQBUF).success) { backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but cursor blit failed"); return false; } diff --git a/src/backend/drm/Renderer.cpp b/src/backend/drm/Renderer.cpp index 7385d3a..f873223 100644 --- a/src/backend/drm/Renderer.cpp +++ b/src/backend/drm/Renderer.cpp @@ -223,6 +223,20 @@ SP CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointeregl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); + loadGLProc(&renderer->egl.eglDestroySyncKHR, "eglDestroySyncKHR"); + loadGLProc(&renderer->egl.eglWaitSyncKHR, "eglWaitSyncKHR"); + loadGLProc(&renderer->egl.eglCreateSyncKHR, "eglCreateSyncKHR"); + loadGLProc(&renderer->egl.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); + + if (!renderer->egl.eglCreateSyncKHR) { + backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglCreateSyncKHR"); + return nullptr; + } + + if (!renderer->egl.eglDupNativeFenceFDANDROID) { + backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglDupNativeFenceFDANDROID"); + return nullptr; + } if (!renderer->egl.eglGetPlatformDisplayEXT) { backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT"); @@ -469,12 +483,94 @@ inline const float fullVerts[] = { 0, 1, // bottom left }; -bool CDRMRenderer::blit(SP from, SP to) { +void CDRMRenderer::waitOnSync(int fd) { + TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (waitOnSync): attempting to wait on fd {}", fd))); + + std::vector attribs; + int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + if (dupFd < 0) { + backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to dup fd for wait"); + return; + } + + attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID); + attribs.push_back(dupFd); + attribs.push_back(EGL_NONE); + + EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data()); + if (sync == EGL_NO_SYNC_KHR) { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to create an egl sync for explicit")); + if (dupFd >= 0) + close(dupFd); + return; + } + + // we got a sync, now we just tell egl to wait before sampling + if (egl.eglWaitSyncKHR(egl.display, sync, 0) != EGL_TRUE) { + if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync")); + + TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to wait on the sync object")); + return; + } + + if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync")); +} + +int CDRMRenderer::recreateBlitSync() { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): recreating blit sync")); + + if (egl.lastBlitSync) { + TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): cleaning up old sync (fd {})", egl.lastBlitSyncFD))); + + // cleanup last sync + if (egl.eglDestroySyncKHR(egl.display, egl.lastBlitSync) != EGL_TRUE) + TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy old sync")); + + if (egl.lastBlitSyncFD >= 0) + close(egl.lastBlitSyncFD); + + egl.lastBlitSyncFD = -1; + egl.lastBlitSync = nullptr; + } + + EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to create an egl sync for explicit")); + return -1; + } + + // we need to flush otherwise we might not get a valid fd + glFlush(); + + int fd = egl.eglDupNativeFenceFDANDROID(egl.display, sync); + if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to dup egl fence fd")); + if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy new sync")); + return -1; + } + + egl.lastBlitSync = sync; + egl.lastBlitSyncFD = fd; + + TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): success, new fence exported with fd {}", fd))); + + return fd; +} + +CDRMRenderer::SBlitResult CDRMRenderer::blit(SP from, SP to, int waitFD) { setEGL(); if (from->dmabuf().size != to->dmabuf().size) { backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched"); - return false; + return {}; + } + + if (waitFD >= 0) { + // wait on a provided explicit fence + waitOnSync(waitFD); } // firstly, get a texture from the from buffer @@ -514,7 +610,7 @@ bool CDRMRenderer::blit(SP from, SP to) { if (!verifyDestinationDMABUF(toDma)) { backend->log(AQ_LOG_ERROR, "EGL (blit): failed to blit: destination dmabuf unsupported"); - return false; + return {}; } { @@ -533,7 +629,7 @@ bool CDRMRenderer::blit(SP from, SP to) { rboImage = createEGLImage(toDma); if (rboImage == EGL_NO_IMAGE_KHR) { backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError())); - return false; + return {}; } GLCALL(glGenRenderbuffers(1, &rboID)); @@ -547,7 +643,7 @@ bool CDRMRenderer::blit(SP from, SP to) { if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError())); - return false; + return {}; } // should never remove anything, but JIC. We'll leak an RBO and FBO if this removes anything. @@ -623,15 +719,19 @@ bool CDRMRenderer::blit(SP from, SP to) { GLCALL(glBindTexture(fromTex.target, 0)); // rendered, cleanup - glFlush(); + // get an explicit sync fd for the secondary gpu. + // when we pass buffers between gpus we should always use explicit sync, + // as implicit is not guaranteed at all + int explicitFD = recreateBlitSync(); + GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); restoreEGL(); - return true; + return {true, explicitFD == -1 ? std::nullopt : std::optional{explicitFD}}; } void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) { diff --git a/src/backend/drm/Renderer.hpp b/src/backend/drm/Renderer.hpp index 0010ee3..b7dbe57 100644 --- a/src/backend/drm/Renderer.hpp +++ b/src/backend/drm/Renderer.hpp @@ -47,12 +47,17 @@ namespace Aquamarine { int drmFD = -1; - bool blit(Hyprutils::Memory::CSharedPointer from, Hyprutils::Memory::CSharedPointer to); + struct SBlitResult { + bool success = false; + std::optional syncFD; + }; - void setEGL(); - void restoreEGL(); + SBlitResult blit(Hyprutils::Memory::CSharedPointer from, Hyprutils::Memory::CSharedPointer to, int waitFD = -1); - void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment); + void setEGL(); + void restoreEGL(); + + void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment); struct { struct SShader { @@ -62,8 +67,10 @@ namespace Aquamarine { } gl; struct { - EGLDisplay display = nullptr; - EGLContext context = nullptr; + EGLDisplay display = nullptr; + EGLContext context = nullptr; + EGLSync lastBlitSync = nullptr; + int lastBlitSyncFD = -1; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; @@ -72,6 +79,10 @@ namespace Aquamarine { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr; PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr; + PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr; + PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr; + PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr; } egl; struct { @@ -92,6 +103,8 @@ namespace Aquamarine { std::optional>> getModsForFormat(EGLint format); bool initDRMFormats(); bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs); + void waitOnSync(int fd); + int recreateBlitSync(); bool hasModifiers = false; Hyprutils::Memory::CWeakPointer backend;