File indexing completed on 2024-04-14 15:33:28

0001 /*
0002     SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "glhelpers.h"
0008 #include <QList>
0009 #include <QVersionNumber>
0010 #include <epoxy/gl.h>
0011 #include <libdrm/drm_fourcc.h>
0012 #include <logging.h>
0013 #include <mutex>
0014 
0015 #include <gbm.h>
0016 
0017 namespace GLHelpers
0018 {
0019 
0020 void initDebugOutputOnce()
0021 {
0022     auto callback = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
0023         Q_UNUSED(source)
0024         Q_UNUSED(severity)
0025         Q_UNUSED(userParam)
0026         while (length && std::isspace(message[length - 1])) {
0027             --length;
0028         }
0029 
0030         switch (type) {
0031         case GL_DEBUG_TYPE_ERROR:
0032         case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
0033             qCWarning(PIPEWIRE_LOGGING, "%#x: %.*s", id, length, message);
0034             break;
0035 
0036         case GL_DEBUG_TYPE_OTHER:
0037         case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
0038         case GL_DEBUG_TYPE_PORTABILITY:
0039         case GL_DEBUG_TYPE_PERFORMANCE:
0040         default:
0041             qCDebug(PIPEWIRE_LOGGING, "%#x: %.*s", id, length, message);
0042             break;
0043         }
0044     };
0045     glDebugMessageCallback(callback, nullptr);
0046     glEnable(GL_DEBUG_OUTPUT);
0047 }
0048 
0049 static std::once_flag initDebugOnce;
0050 void initDebugOutput()
0051 {
0052     if (!PIPEWIRE_LOGGING().isDebugEnabled()) {
0053         return;
0054     }
0055 
0056     if (!eglGetCurrentDisplay()) {
0057         // Epoxy gets very confused and it will crash
0058         return;
0059     }
0060 
0061     std::call_once(initDebugOnce, initDebugOutputOnce);
0062 }
0063 
0064 #define ENUM_STRING(x) case x: return #x;
0065 
0066 QByteArray formatGLError(GLenum err)
0067 {
0068     switch (err) {
0069     ENUM_STRING(GL_NO_ERROR)
0070     ENUM_STRING(GL_INVALID_ENUM)
0071     ENUM_STRING(GL_INVALID_VALUE)
0072     ENUM_STRING(GL_INVALID_OPERATION)
0073     ENUM_STRING(GL_STACK_OVERFLOW)
0074     ENUM_STRING(GL_STACK_UNDERFLOW)
0075     ENUM_STRING(GL_OUT_OF_MEMORY)
0076     default:
0077         return QByteArray("0x") + QByteArray::number(err, 16);
0078     }
0079 }
0080 
0081 QByteArray formatEGLError(GLenum err)
0082 {
0083     switch (err) {
0084         ENUM_STRING(EGL_SUCCESS)
0085         ENUM_STRING(EGL_BAD_DISPLAY)
0086         ENUM_STRING(EGL_BAD_CONTEXT)
0087         ENUM_STRING(EGL_BAD_PARAMETER)
0088         ENUM_STRING(EGL_BAD_MATCH)
0089         ENUM_STRING(EGL_BAD_ACCESS)
0090         ENUM_STRING(EGL_BAD_ALLOC)
0091         ENUM_STRING(EGL_BAD_CONFIG)
0092     default:
0093         return QByteArray("0x") + QByteArray::number(err, 16);
0094     }
0095 }
0096 
0097 EGLImage createImage(EGLDisplay display, const DmaBufAttributes &dmabufAttribs, uint32_t format, const QSize &size, gbm_device *gbmDevice)
0098 {
0099     Q_ASSERT(!size.isEmpty());
0100     gbm_bo *imported = nullptr;
0101     if (gbmDevice) {
0102         gbm_import_fd_data importInfo = {static_cast<int>(dmabufAttribs.planes[0].fd),
0103                                          static_cast<uint32_t>(size.width()),
0104                                          static_cast<uint32_t>(size.height()),
0105                                          static_cast<uint32_t>(dmabufAttribs.planes[0].stride),
0106                                          GBM_BO_FORMAT_ARGB8888};
0107         imported = gbm_bo_import(gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT);
0108         if (!imported) {
0109             qCWarning(PIPEWIRE_LOGGING) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno);
0110             return EGL_NO_IMAGE_KHR;
0111         }
0112     }
0113 
0114     const bool hasModifiers = dmabufAttribs.modifier != DRM_FORMAT_MOD_INVALID;
0115 
0116     QVector<EGLint> attribs;
0117     attribs << EGL_WIDTH << size.width() << EGL_HEIGHT << size.height() << EGL_LINUX_DRM_FOURCC_EXT << EGLint(format)
0118 
0119             << EGL_DMA_BUF_PLANE0_FD_EXT << dmabufAttribs.planes[0].fd << EGL_DMA_BUF_PLANE0_OFFSET_EXT << EGLint(dmabufAttribs.planes[0].offset)
0120             << EGL_DMA_BUF_PLANE0_PITCH_EXT << EGLint(dmabufAttribs.planes[0].stride);
0121 
0122     if (hasModifiers) {
0123         attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT
0124                 << EGLint(dmabufAttribs.modifier >> 32);
0125     }
0126 
0127     if (dmabufAttribs.planes.count() > 1) {
0128         attribs << EGL_DMA_BUF_PLANE1_FD_EXT << dmabufAttribs.planes[1].fd << EGL_DMA_BUF_PLANE1_OFFSET_EXT << EGLint(dmabufAttribs.planes[1].offset)
0129                 << EGL_DMA_BUF_PLANE1_PITCH_EXT << EGLint(dmabufAttribs.planes[1].stride);
0130 
0131         if (hasModifiers) {
0132             attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT
0133                     << EGLint(dmabufAttribs.modifier >> 32);
0134         }
0135     }
0136 
0137     if (dmabufAttribs.planes.count() > 2) {
0138         attribs << EGL_DMA_BUF_PLANE2_FD_EXT << dmabufAttribs.planes[2].fd << EGL_DMA_BUF_PLANE2_OFFSET_EXT << EGLint(dmabufAttribs.planes[2].offset)
0139                 << EGL_DMA_BUF_PLANE2_PITCH_EXT << EGLint(dmabufAttribs.planes[2].stride);
0140 
0141         if (hasModifiers) {
0142             attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT
0143                     << EGLint(dmabufAttribs.modifier >> 32);
0144         }
0145     }
0146 
0147     if (dmabufAttribs.planes.count() > 3) {
0148         attribs << EGL_DMA_BUF_PLANE3_FD_EXT << dmabufAttribs.planes[3].fd << EGL_DMA_BUF_PLANE3_OFFSET_EXT << EGLint(dmabufAttribs.planes[3].offset)
0149                 << EGL_DMA_BUF_PLANE3_PITCH_EXT << EGLint(dmabufAttribs.planes[3].stride);
0150 
0151         if (hasModifiers) {
0152             attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabufAttribs.modifier & 0xffffffff) << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT
0153                     << EGLint(dmabufAttribs.modifier >> 32);
0154         }
0155     }
0156 
0157     attribs << EGL_NONE;
0158 
0159     static auto eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
0160 
0161     /* MESA says:
0162      * "If <target> is EGL_LINUX_DMA_BUF_EXT, <dpy> must be a valid display,
0163      *  <ctx> must be EGL_NO_CONTEXT..."
0164      */
0165     EGLImage ret = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, imported, attribs.data());
0166     if (ret == EGL_NO_IMAGE_KHR) {
0167         qCWarning(PIPEWIRE_LOGGING) << "invalid image" << GLHelpers::formatEGLError(eglGetError());
0168     }
0169     if (imported) {
0170         gbm_bo_destroy(imported);
0171     }
0172     return ret;
0173 }
0174 }