Skip to content

Commit

Permalink
drm: s
Browse files Browse the repository at this point in the history
upport explicit sync with multi-gpu destinations

will break o
n mgpu nvidia before 560 driver
  • Loading branch information
vaxerski committed Aug 4, 2024
1 parent a70fc6a commit 6f5adc0
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 16 deletions.
14 changes: 11 additions & 3 deletions src/backend/drm/DRM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
114 changes: 107 additions & 7 deletions src/backend/drm/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {};
}

{
Expand All @@ -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));
Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
25 changes: 19 additions & 6 deletions src/backend/drm/Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -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;
Expand Down

0 comments on commit 6f5adc0

Please sign in to comment.