| 1 | // Copyright (C) 2017 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 3 | |
| 4 | #include "drmeglserverbufferintegration.h" |
| 5 | |
| 6 | #include <QtOpenGL/QOpenGLTexture> |
| 7 | #include <QtGui/QOpenGLContext> |
| 8 | |
| 9 | QT_BEGIN_NAMESPACE |
| 10 | |
| 11 | DrmEglServerBuffer::DrmEglServerBuffer(DrmEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format) |
| 12 | : QtWayland::ServerBuffer(qimage.size(),format) |
| 13 | , m_integration(integration) |
| 14 | { |
| 15 | m_format = format; |
| 16 | |
| 17 | EGLint egl_format; |
| 18 | switch (m_format) { |
| 19 | case RGBA32: |
| 20 | m_drm_format = QtWaylandServer::qt_drm_egl_server_buffer::format_RGBA32; |
| 21 | egl_format = EGL_DRM_BUFFER_FORMAT_ARGB32_MESA; |
| 22 | break; |
| 23 | #ifdef EGL_DRM_BUFFER_FORMAT_A8_MESA |
| 24 | case A8: |
| 25 | m_drm_format = QtWaylandServer::qt_drm_egl_server_buffer::format_A8; |
| 26 | egl_format = EGL_DRM_BUFFER_FORMAT_A8_MESA; |
| 27 | break; |
| 28 | #endif |
| 29 | default: |
| 30 | qWarning(msg: "DrmEglServerBuffer: unsupported format" ); |
| 31 | m_drm_format = QtWaylandServer::qt_drm_egl_server_buffer::format_RGBA32; |
| 32 | egl_format = EGL_DRM_BUFFER_FORMAT_ARGB32_MESA; |
| 33 | break; |
| 34 | } |
| 35 | EGLint imageAttribs[] = { |
| 36 | EGL_WIDTH, m_size.width(), |
| 37 | EGL_HEIGHT, m_size.height(), |
| 38 | EGL_DRM_BUFFER_FORMAT_MESA, egl_format, |
| 39 | EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SHARE_MESA, |
| 40 | EGL_NONE |
| 41 | }; |
| 42 | |
| 43 | m_image = m_integration->eglCreateDRMImageMESA(attrib_list: imageAttribs); |
| 44 | |
| 45 | EGLint handle; |
| 46 | if (!m_integration->eglExportDRMImageMESA(image: m_image, name: &m_name, handle: &handle, stride: &m_stride)) { |
| 47 | qWarning(msg: "DrmEglServerBuffer: Failed to export egl image" ); |
| 48 | } |
| 49 | |
| 50 | m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); |
| 51 | m_texture->create(); |
| 52 | m_texture->bind(); |
| 53 | |
| 54 | m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image: m_image); |
| 55 | |
| 56 | glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: 0, yoffset: 0, width: qimage.width(), height: qimage.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels: qimage.constBits()); |
| 57 | |
| 58 | m_texture->release(); |
| 59 | m_texture->setSize(width: m_size.width(), height: m_size.height()); |
| 60 | } |
| 61 | |
| 62 | struct ::wl_resource *DrmEglServerBuffer::resourceForClient(struct ::wl_client *client) |
| 63 | { |
| 64 | auto *bufferResource = resourceMap().value(client); |
| 65 | if (!bufferResource) { |
| 66 | auto integrationResource = m_integration->resourceMap().value(client); |
| 67 | if (!integrationResource) { |
| 68 | qWarning(msg: "DrmEglServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the drm_egl interface" ); |
| 69 | return nullptr; |
| 70 | } |
| 71 | struct ::wl_resource *drm_egl_integration_resource = integrationResource->handle; |
| 72 | Resource *resource = add(client, 1); |
| 73 | m_integration->send_server_buffer_created(drm_egl_integration_resource, resource->handle, m_name, m_size.width(), m_size.height(), m_stride, m_drm_format); |
| 74 | return resource->handle; |
| 75 | } |
| 76 | return bufferResource->handle; |
| 77 | } |
| 78 | |
| 79 | |
| 80 | QOpenGLTexture *DrmEglServerBuffer::toOpenGlTexture() |
| 81 | { |
| 82 | if (!m_texture) { |
| 83 | qWarning(msg: "DrmEglServerBuffer::toOpenGlTexture: no texture defined" ); |
| 84 | } |
| 85 | return m_texture; |
| 86 | } |
| 87 | |
| 88 | bool DrmEglServerBuffer::bufferInUse() |
| 89 | { |
| 90 | return resourceMap().size() > 0; |
| 91 | } |
| 92 | |
| 93 | DrmEglServerBufferIntegration::DrmEglServerBufferIntegration() |
| 94 | { |
| 95 | } |
| 96 | |
| 97 | DrmEglServerBufferIntegration::~DrmEglServerBufferIntegration() |
| 98 | { |
| 99 | } |
| 100 | |
| 101 | bool DrmEglServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor) |
| 102 | { |
| 103 | Q_ASSERT(QGuiApplication::platformNativeInterface()); |
| 104 | |
| 105 | m_egl_display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration(resource: "egldisplay" )); |
| 106 | if (!m_egl_display) { |
| 107 | qWarning(msg: "Can't initialize drm egl server buffer integration. Missing egl display from platform plugin" ); |
| 108 | return false; |
| 109 | } |
| 110 | |
| 111 | const char *extensionString = eglQueryString(dpy: m_egl_display, EGL_EXTENSIONS); |
| 112 | if (!extensionString || !strstr(haystack: extensionString, needle: "EGL_KHR_image" )) { |
| 113 | qWarning(msg: "Failed to initialize drm egl server buffer integration. There is no EGL_KHR_image extension.\n" ); |
| 114 | return false; |
| 115 | } |
| 116 | m_egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress(procname: "eglCreateImageKHR" )); |
| 117 | m_egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress(procname: "eglDestroyImageKHR" )); |
| 118 | if (!m_egl_create_image || !m_egl_destroy_image) { |
| 119 | qWarning(msg: "Failed to initialize drm egl server buffer integration. Could not resolve eglCreateImageKHR or eglDestroyImageKHR" ); |
| 120 | return false; |
| 121 | } |
| 122 | |
| 123 | if (!extensionString || !strstr(haystack: extensionString, needle: "EGL_MESA_drm_image" )) { |
| 124 | qWarning(msg: "Failed to initialize drm egl server buffer integration. There is no EGL_MESA_drm_image extension.\n" ); |
| 125 | return false; |
| 126 | } |
| 127 | |
| 128 | m_egl_create_drm_image = reinterpret_cast<PFNEGLCREATEDRMIMAGEMESAPROC>(eglGetProcAddress(procname: "eglCreateDRMImageMESA" )); |
| 129 | m_egl_export_drm_image = reinterpret_cast<PFNEGLEXPORTDRMIMAGEMESAPROC>(eglGetProcAddress(procname: "eglExportDRMImageMESA" )); |
| 130 | if (!m_egl_create_drm_image || !m_egl_export_drm_image) { |
| 131 | qWarning(msg: "Failed to initialize drm egl server buffer integration. Could not find eglCreateDRMImageMESA or eglExportDRMImageMESA.\n" ); |
| 132 | return false; |
| 133 | } |
| 134 | |
| 135 | m_gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES" )); |
| 136 | if (!m_gl_egl_image_target_texture_2d) { |
| 137 | qWarning(msg: "Failed to initialize drm egl server buffer integration. Could not find glEGLImageTargetTexture2DOES.\n" ); |
| 138 | return false; |
| 139 | } |
| 140 | |
| 141 | QtWaylandServer::qt_drm_egl_server_buffer::init(compositor->display(), 1); |
| 142 | return true; |
| 143 | } |
| 144 | |
| 145 | bool DrmEglServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const |
| 146 | { |
| 147 | switch (format) { |
| 148 | case QtWayland::ServerBuffer::RGBA32: |
| 149 | return true; |
| 150 | case QtWayland::ServerBuffer::A8: |
| 151 | #ifdef EGL_DRM_BUFFER_FORMAT_A8_MESA |
| 152 | return true; |
| 153 | #else |
| 154 | return false; |
| 155 | #endif |
| 156 | default: |
| 157 | return false; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | QtWayland::ServerBuffer *DrmEglServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) |
| 162 | { |
| 163 | return new DrmEglServerBuffer(this, qimage, format); |
| 164 | } |
| 165 | |
| 166 | QT_END_NAMESPACE |
| 167 | |