mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-11-17 06:06:00 +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
|
@ -1475,11 +1475,19 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NEWAQBUF = mgpu.swapchain->next(nullptr);
|
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");
|
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed");
|
||||||
return false;
|
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
|
drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
|
||||||
} else
|
} else
|
||||||
drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present
|
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);
|
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");
|
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but cursor blit failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,20 @@ SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAll
|
||||||
loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
|
loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
|
||||||
loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT");
|
loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT");
|
||||||
loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT");
|
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) {
|
if (!renderer->egl.eglGetPlatformDisplayEXT) {
|
||||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT");
|
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT");
|
||||||
|
@ -469,12 +483,94 @@ inline const float fullVerts[] = {
|
||||||
0, 1, // bottom left
|
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();
|
setEGL();
|
||||||
|
|
||||||
if (from->dmabuf().size != to->dmabuf().size) {
|
if (from->dmabuf().size != to->dmabuf().size) {
|
||||||
backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched");
|
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
|
// firstly, get a texture from the from buffer
|
||||||
|
@ -514,7 +610,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
|
||||||
|
|
||||||
if (!verifyDestinationDMABUF(toDma)) {
|
if (!verifyDestinationDMABUF(toDma)) {
|
||||||
backend->log(AQ_LOG_ERROR, "EGL (blit): failed to blit: destination dmabuf unsupported");
|
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);
|
rboImage = createEGLImage(toDma);
|
||||||
if (rboImage == EGL_NO_IMAGE_KHR) {
|
if (rboImage == EGL_NO_IMAGE_KHR) {
|
||||||
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
|
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
GLCALL(glGenRenderbuffers(1, &rboID));
|
GLCALL(glGenRenderbuffers(1, &rboID));
|
||||||
|
@ -547,7 +643,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
|
||||||
|
|
||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError()));
|
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.
|
// 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));
|
GLCALL(glBindTexture(fromTex.target, 0));
|
||||||
|
|
||||||
// rendered, cleanup
|
// rendered, cleanup
|
||||||
|
|
||||||
glFlush();
|
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(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||||
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
||||||
|
|
||||||
restoreEGL();
|
restoreEGL();
|
||||||
|
|
||||||
return true;
|
return {true, explicitFD == -1 ? std::nullopt : std::optional<int>{explicitFD}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {
|
void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {
|
||||||
|
|
|
@ -47,7 +47,12 @@ namespace Aquamarine {
|
||||||
|
|
||||||
int drmFD = -1;
|
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 setEGL();
|
||||||
void restoreEGL();
|
void restoreEGL();
|
||||||
|
@ -64,6 +69,8 @@ namespace Aquamarine {
|
||||||
struct {
|
struct {
|
||||||
EGLDisplay display = nullptr;
|
EGLDisplay display = nullptr;
|
||||||
EGLContext context = nullptr;
|
EGLContext context = nullptr;
|
||||||
|
EGLSync lastBlitSync = nullptr;
|
||||||
|
int lastBlitSyncFD = -1;
|
||||||
|
|
||||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
|
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
|
||||||
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr;
|
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr;
|
||||||
|
@ -72,6 +79,10 @@ namespace Aquamarine {
|
||||||
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
|
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
|
||||||
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr;
|
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr;
|
||||||
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
||||||
|
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr;
|
||||||
|
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr;
|
||||||
|
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr;
|
||||||
|
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr;
|
||||||
} egl;
|
} egl;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -92,6 +103,8 @@ namespace Aquamarine {
|
||||||
std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
|
std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
|
||||||
bool initDRMFormats();
|
bool initDRMFormats();
|
||||||
bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
|
bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
|
||||||
|
void waitOnSync(int fd);
|
||||||
|
int recreateBlitSync();
|
||||||
bool hasModifiers = false;
|
bool hasModifiers = false;
|
||||||
|
|
||||||
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
||||||
|
|
Loading…
Reference in a new issue