upport explicit sync with multi-gpu destinations

will break o
n mgpu nvidia before 560 driver
This commit is contained in:
Vaxry 2024-08-05 00:27:09 +02:00
parent a70fc6a2fd
commit 6f5adc0568
3 changed files with 137 additions and 16 deletions

View file

@ -1475,11 +1475,19 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
}
auto NEWAQBUF = mgpu.swapchain->next(nullptr);
if (!backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF)) {
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<IBuffer> 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;
}

View file

@ -223,6 +223,20 @@ SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAll
loadGLProc(&renderer->egl.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<IBuffer> from, SP<IBuffer> to) {
void CDRMRenderer::waitOnSync(int fd) {
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (waitOnSync): attempting to wait on fd {}", fd)));
std::vector<EGLint> 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<IBuffer> from, SP<IBuffer> 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<IBuffer> from, SP<IBuffer> 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<IBuffer> from, SP<IBuffer> 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<IBuffer> from, SP<IBuffer> 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<IBuffer> from, SP<IBuffer> 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<int>{explicitFD}};
}
void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {

View file

@ -47,7 +47,12 @@ namespace Aquamarine {
int drmFD = -1;
bool blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to);
struct SBlitResult {
bool success = false;
std::optional<int> syncFD;
};
SBlitResult blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to, int waitFD = -1);
void setEGL();
void restoreEGL();
@ -64,6 +69,8 @@ namespace Aquamarine {
struct {
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<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
bool initDRMFormats();
bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
void waitOnSync(int fd);
int recreateBlitSync();
bool hasModifiers = false;
Hyprutils::Memory::CWeakPointer<CBackend> backend;