From d619af49718a356b9b4ab10c75550451e9888f64 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 11 Jul 2024 22:17:19 +0200 Subject: [PATCH] drm: better handle drm blits --- include/aquamarine/misc/Attachment.hpp | 1 + src/backend/drm/DRM.cpp | 2 + src/backend/drm/Math.hpp | 18 ++-- src/backend/drm/Renderer.cpp | 143 ++++++++++++++++++------- src/backend/drm/Renderer.hpp | 24 ++++- 5 files changed, 142 insertions(+), 46 deletions(-) diff --git a/include/aquamarine/misc/Attachment.hpp b/include/aquamarine/misc/Attachment.hpp index 94cb47e..37a6163 100644 --- a/include/aquamarine/misc/Attachment.hpp +++ b/include/aquamarine/misc/Attachment.hpp @@ -7,6 +7,7 @@ namespace Aquamarine { enum eAttachmentType : uint32_t { AQ_ATTACHMENT_DRM_BUFFER = 0, AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE, + AQ_ATTACHMENT_DRM_RENDERER_DATA, }; class IAttachment { diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 58c62d9..6f11b41 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -496,6 +496,8 @@ bool Aquamarine::CDRMBackend::initMgpu() { return false; } + mgpu.renderer->self = mgpu.renderer; + return true; } diff --git a/src/backend/drm/Math.hpp b/src/backend/drm/Math.hpp index 7cef791..999150a 100644 --- a/src/backend/drm/Math.hpp +++ b/src/backend/drm/Math.hpp @@ -7,12 +7,12 @@ using namespace Hyprutils::Math; -void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]); -void matrixProjection(float mat[9], int width, int height, eTransform transform); -void matrixTransform(float mat[9], eTransform transform); -void matrixRotate(float mat[9], float rad); -void matrixScale(float mat[9], float x, float y); -void matrixTranslate(float mat[9], float x, float y); -void matrixTranspose(float mat[9], const float a[9]); -void matrixMultiply(float mat[9], const float a[9], const float b[9]); -void matrixIdentity(float mat[9]); +void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]); +void matrixProjection(float mat[9], int width, int height, eTransform transform); +void matrixTransform(float mat[9], eTransform transform); +void matrixRotate(float mat[9], float rad); +void matrixScale(float mat[9], float x, float y); +void matrixTranslate(float mat[9], float x, float y); +void matrixTranspose(float mat[9], const float a[9]); +void matrixMultiply(float mat[9], const float a[9], const float b[9]); +void matrixIdentity(float mat[9]); diff --git a/src/backend/drm/Renderer.cpp b/src/backend/drm/Renderer.cpp index 5f0ebf9..9ab60d5 100644 --- a/src/backend/drm/Renderer.cpp +++ b/src/backend/drm/Renderer.cpp @@ -5,6 +5,7 @@ #include #include "Math.hpp" #include "Shared.hpp" +#include "FormatUtils.hpp" using namespace Aquamarine; using namespace Hyprutils::Memory; @@ -276,6 +277,8 @@ EGLImageKHR CDRMRenderer::createEGLImage(const SDMABUFAttrs& attrs) { attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT); attribs.push_back(attrs.format); + TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: createEGLImage: size {} with format {}", attrs.size, fourccToName(attrs.format)))); + struct { EGLint fd; EGLint offset; @@ -364,38 +367,78 @@ bool CDRMRenderer::blit(SP from, SP to) { } // firstly, get a texture from the from buffer - auto fromTex = glTex(from); + // if it has an attachment, use that + // both from and to have the same AQ_ATTACHMENT_DRM_RENDERER_DATA. + // Those buffers always come from different swapchains, so it's OK. + + GLTex fromTex; + { + auto attachment = from->attachments.get(AQ_ATTACHMENT_DRM_RENDERER_DATA); + if (attachment) { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (blit): From attachment found")); + auto att = (CDRMRendererBufferAttachment*)attachment.get(); + fromTex = {att->eglImage, att->texid}; + } + + if (!fromTex.image) { + backend->log(AQ_LOG_DEBUG, "EGL (blit): No attachment in from, creating a new image"); + fromTex = glTex(from); + + // should never remove anything, but JIC. We'll leak an EGLImage if this removes anything. + from->attachments.removeByType(AQ_ATTACHMENT_DRM_RENDERER_DATA); + from->attachments.add(makeShared(self, from, fromTex.image, 0, 0, fromTex.texid)); + } + } TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): fromTex id {}, image 0x{:x}", fromTex.texid, (uintptr_t)fromTex.image))); // then, get a rbo from our to buffer - auto toDma = to->dmabuf(); + // if it has an attachment, use that - auto rboImage = createEGLImage(toDma); - if (rboImage == EGL_NO_IMAGE_KHR) { - backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError())); - return false; + EGLImageKHR rboImage = nullptr; + GLuint rboID = 0, fboID = 0; + auto toDma = to->dmabuf(); + { + auto attachment = to->attachments.get(AQ_ATTACHMENT_DRM_RENDERER_DATA); + if (attachment) { + TRACE(backend->log(AQ_LOG_TRACE, "EGL (blit): To attachment found")); + auto att = (CDRMRendererBufferAttachment*)attachment.get(); + rboImage = att->eglImage; + fboID = att->fbo; + rboID = att->rbo; + } + + if (!rboImage) { + backend->log(AQ_LOG_DEBUG, "EGL (blit): No attachment in to, creating a new image"); + + rboImage = createEGLImage(toDma); + if (rboImage == EGL_NO_IMAGE_KHR) { + backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError())); + return false; + } + + GLCALL(glGenRenderbuffers(1, &rboID)); + GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID)); + egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage); + GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + GLCALL(glGenFramebuffers(1, &fboID)); + GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID)); + GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID)); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError())); + return false; + } + + // should never remove anything, but JIC. We'll leak an RBO and FBO if this removes anything. + to->attachments.removeByType(AQ_ATTACHMENT_DRM_RENDERER_DATA); + to->attachments.add(makeShared(self, to, rboImage, fboID, rboID, 0)); + } } TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): rboImage 0x{:x}", (uintptr_t)rboImage))); - GLuint rboID = 0, fboID = 0; - - // TODO: don't spam this? - GLCALL(glGenRenderbuffers(1, &rboID)); - GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID)); - egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage); - GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - GLCALL(glGenFramebuffers(1, &fboID)); - GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID)); - GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID)); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError())); - return false; - } - GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID)); GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID)); @@ -410,12 +453,18 @@ bool CDRMRenderer::blit(SP from, SP to) { TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): box size {}", renderBox.size()))); float mtx[9]; - float identity[9]; + float base[9]; float monitorProj[9]; - matrixIdentity(identity); - projectBox(mtx, renderBox, HYPRUTILS_TRANSFORM_NORMAL, 0, identity); + matrixIdentity(base); - matrixProjection(monitorProj, toDma.size.x, toDma.size.y, HYPRUTILS_TRANSFORM_NORMAL); + // KMS uses flipped y, we have to do FLIPPED_180 + matrixTranslate(base, toDma.size.x / 2.0, toDma.size.y / 2.0); + matrixTransform(base, HYPRUTILS_TRANSFORM_FLIPPED_180); + matrixTranslate(base, -toDma.size.x / 2.0, -toDma.size.y / 2.0); + + projectBox(mtx, renderBox, HYPRUTILS_TRANSFORM_FLIPPED_180, 0, base); + + matrixProjection(monitorProj, toDma.size.x, toDma.size.y, HYPRUTILS_TRANSFORM_FLIPPED_180); float glMtx[9]; matrixMultiply(glMtx, monitorProj, mtx); @@ -424,6 +473,10 @@ bool CDRMRenderer::blit(SP from, SP to) { GLCALL(glActiveTexture(GL_TEXTURE0)); GLCALL(glBindTexture(GL_TEXTURE_2D, fromTex.texid)); + + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GLCALL(glUseProgram(gl.shader.program)); GLCALL(glDisable(GL_BLEND)); GLCALL(glDisable(GL_SCISSOR_TEST)); @@ -448,18 +501,36 @@ bool CDRMRenderer::blit(SP from, SP to) { // rendered, cleanup - GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + glFlush(); + GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - - GLCALL(glDeleteTextures(1, &fromTex.texid)); - - GLCALL(glDeleteRenderbuffers(1, &rboID)); - GLCALL(glDeleteFramebuffers(1, &fboID)); - - egl.eglDestroyImageKHR(egl.display, rboImage); - egl.eglDestroyImageKHR(egl.display, fromTex.image); + GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); restoreEGL(); return true; } + +void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) { + setEGL(); + + TRACE(backend->log(AQ_LOG_TRACE, + std::format("EGL (onBufferAttachmentDrop): dropping fbo {} rbo {} image 0x{:x}", attachment->fbo, attachment->rbo, (uintptr_t)attachment->eglImage))); + + if (attachment->texid) + GLCALL(glDeleteTextures(1, &attachment->texid)); + if (attachment->rbo) + GLCALL(glDeleteRenderbuffers(1, &attachment->rbo)); + if (attachment->fbo) + GLCALL(glDeleteFramebuffers(1, &attachment->fbo)); + if (attachment->eglImage) + egl.eglDestroyImageKHR(egl.display, attachment->eglImage); + + restoreEGL(); +} + +CDRMRendererBufferAttachment::CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer renderer_, Hyprutils::Memory::CSharedPointer buffer, + EGLImageKHR image, GLuint fbo_, GLuint rbo_, GLuint texid_) : + eglImage(image), fbo(fbo_), rbo(rbo_), renderer(renderer_), texid(texid_) { + bufferDestroy = buffer->events.destroy.registerListener([this](std::any d) { renderer->onBufferAttachmentDrop(this); }); +} diff --git a/src/backend/drm/Renderer.hpp b/src/backend/drm/Renderer.hpp index 683ddb3..02affe5 100644 --- a/src/backend/drm/Renderer.hpp +++ b/src/backend/drm/Renderer.hpp @@ -8,6 +8,24 @@ #include namespace Aquamarine { + class CDRMRendererBufferAttachment : public IAttachment { + public: + CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer renderer_, Hyprutils::Memory::CSharedPointer buffer, EGLImageKHR image, GLuint fbo_, + GLuint rbo_, GLuint texid_); + virtual ~CDRMRendererBufferAttachment() { + ; + } + virtual eAttachmentType type() { + return AQ_ATTACHMENT_DRM_RENDERER_DATA; + } + + EGLImageKHR eglImage = nullptr; + GLuint fbo = 0, rbo = 0, texid = 0; + Hyprutils::Signal::CHyprSignalListener bufferDestroy; + + Hyprutils::Memory::CWeakPointer renderer; + }; + class CDRMRenderer { public: static Hyprutils::Memory::CSharedPointer attempt(int drmfd, Hyprutils::Memory::CSharedPointer backend_); @@ -19,6 +37,8 @@ namespace Aquamarine { void setEGL(); void restoreEGL(); + void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment); + struct { struct { GLuint program = 0; @@ -53,7 +73,9 @@ namespace Aquamarine { GLuint texid = 0; }; - GLTex glTex(Hyprutils::Memory::CSharedPointer buf); + GLTex glTex(Hyprutils::Memory::CSharedPointer buf); + + Hyprutils::Memory::CWeakPointer self; private: CDRMRenderer() = default;