mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-12-22 11:39:49 +01:00
drm: s
upport explicit sync with multi-gpu destinations will break o n mgpu nvidia before 560 driver
This commit is contained in:
parent
a70fc6a2fd
commit
6f5adc0568
3 changed files with 137 additions and 16 deletions
|
@ -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<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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -47,12 +47,17 @@ 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;
|
||||
};
|
||||
|
||||
void setEGL();
|
||||
void restoreEGL();
|
||||
SBlitResult blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> 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<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;
|
||||
|
|
Loading…
Reference in a new issue