diff options
author | David Redondo <[email protected]> | 2025-02-28 12:14:06 +0100 |
---|---|---|
committer | David Redondo <[email protected]> | 2025-03-11 16:45:25 +0100 |
commit | 58e68f1981d0ba0a80e373dd5ca78a9260824c36 (patch) | |
tree | 76c4faafe64ae628f3b5ef75b4649bd4bed9599f | |
parent | ab73b6c286ca79c4ab353e72ce0279ee4f7f5713 (diff) |
Move client hardware integration sources into the plugin folder
Change-Id: I49629ce1d55d8762ab30c33062fb78b05fdb05d4
Reviewed-by: Eskil Abrahamsen Blomfeldt <[email protected]>
31 files changed, 2885 insertions, 24 deletions
diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/CMakeLists.txt index f706632c0a6..ca6c0338614 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/CMakeLists.txt @@ -11,12 +11,10 @@ qt_internal_add_plugin(QWaylandBrcmEglClientBufferPlugin OUTPUT_NAME brcm-egl PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h - ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.h - ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.cpp ../../../../hardwareintegration/client/brcm-egl/qwaylandbrcmglcontext.h + qwaylandbrcmeglintegration.cpp qwaylandbrcmeglintegration.h + qwaylandbrcmeglwindow.cpp qwaylandbrcmeglwindow.h + qwaylandbrcmglcontext.cpp qwaylandbrcmglcontext.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/brcm-egl PUBLIC_LIBRARIES ${CMAKE_DL_LIBS} EGL::EGL @@ -30,7 +28,7 @@ qt_internal_add_plugin(QWaylandBrcmEglClientBufferPlugin qt6_generate_wayland_protocol_client_sources(QWaylandBrcmEglClientBufferPlugin PRIVATE_CODE FILES - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../hardwareintegration/client/brcm-egl/../../../extensions/brcm.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../../../extensions/brcm.xml ) #### Keys ignored in scope 1:.:.:brcm-egl.pro:<TRUE>: diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.cpp new file mode 100644 index 00000000000..8f9047993ed --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.cpp @@ -0,0 +1,121 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandbrcmeglintegration.h" + +#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> + +#include "qwaylandbrcmeglwindow.h" +#include "qwaylandbrcmglcontext.h" + +#include <QtCore/QDebug> + +#include "wayland-brcm-client-protocol.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandBrcmEglIntegration::QWaylandBrcmEglIntegration() +{ + qDebug() << "Using Brcm-EGL"; +} + +void QWaylandBrcmEglIntegration::wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == "qt_brcm") { + QWaylandBrcmEglIntegration *integration = static_cast<QWaylandBrcmEglIntegration *>(data); + integration->m_waylandBrcm = static_cast<struct qt_brcm *>(wl_registry_bind(registry, id, &qt_brcm_interface, 1)); + } +} + +qt_brcm *QWaylandBrcmEglIntegration::waylandBrcm() const +{ + return m_waylandBrcm; +} + +QWaylandBrcmEglIntegration::~QWaylandBrcmEglIntegration() +{ + eglTerminate(m_eglDisplay); +} + +void QWaylandBrcmEglIntegration::initialize(QWaylandDisplay *waylandDisplay) +{ + m_display = waylandDisplay; + m_waylandDisplay = waylandDisplay->wl_display(); + waylandDisplay->addRegistryListener(wlDisplayHandleGlobal, this); + EGLint major,minor; + m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY); + if (m_eglDisplay == NULL) { + qWarning("EGL not available"); + } else { + if (!eglInitialize(m_eglDisplay, &major, &minor)) { + qWarning("failed to initialize EGL display"); + return; + } + + eglFlushBRCM = (PFNEGLFLUSHBRCMPROC)eglGetProcAddress("eglFlushBRCM"); + if (!eglFlushBRCM) { + qWarning("failed to resolve eglFlushBRCM, performance will suffer"); + } + + eglCreateGlobalImageBRCM = (PFNEGLCREATEGLOBALIMAGEBRCMPROC)eglGetProcAddress("eglCreateGlobalImageBRCM"); + if (!eglCreateGlobalImageBRCM) { + qWarning("failed to resolve eglCreateGlobalImageBRCM"); + return; + } + + eglDestroyGlobalImageBRCM = (PFNEGLDESTROYGLOBALIMAGEBRCMPROC)eglGetProcAddress("eglDestroyGlobalImageBRCM"); + if (!eglDestroyGlobalImageBRCM) { + qWarning("failed to resolve eglDestroyGlobalImageBRCM"); + return; + } + } +} + +QWaylandWindow *QWaylandBrcmEglIntegration::createEglWindow(QWindow *window) +{ + return new QWaylandBrcmEglWindow(window, m_display); +} + +QPlatformOpenGLContext *QWaylandBrcmEglIntegration::createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const +{ + return new QWaylandBrcmGLContext(m_eglDisplay, glFormat, share); +} + +EGLDisplay QWaylandBrcmEglIntegration::eglDisplay() const +{ + return m_eglDisplay; +} + +void *QWaylandBrcmEglIntegration::nativeResource(NativeResource resource) +{ + switch (resource) { + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + +void *QWaylandBrcmEglIntegration::nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) +{ + Q_ASSERT(context); + switch (resource) { + case EglConfig: + return static_cast<QWaylandBrcmGLContext *>(context)->eglConfig(); + case EglContext: + return static_cast<QWaylandBrcmGLContext *>(context)->eglContext(); + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.h new file mode 100644 index 00000000000..ac164ab9dac --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglintegration.h @@ -0,0 +1,68 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDBRCMEGLINTEGRATION_H +#define QWAYLANDBRCMEGLINTEGRATION_H + +#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> +#include <wayland-client-core.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <EGL/eglext_brcm.h> + +#include <QtCore/qglobal.h> + +struct qt_brcm; + +QT_BEGIN_NAMESPACE + +class QWindow; + +namespace QtWaylandClient { + +class QWaylandWindow; + +class QWaylandBrcmEglIntegration : public QWaylandClientBufferIntegration +{ +public: + QWaylandBrcmEglIntegration(); + ~QWaylandBrcmEglIntegration(); + + void initialize(QWaylandDisplay *waylandDisplay) override; + + bool supportsThreadedOpenGL() const override { return true; } + bool supportsWindowDecoration() const override { return false; } + + QWaylandWindow *createEglWindow(QWindow *window); + QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const override; + + EGLDisplay eglDisplay() const; + + struct qt_brcm *waylandBrcm() const; + + PFNEGLFLUSHBRCMPROC eglFlushBRCM; + PFNEGLCREATEGLOBALIMAGEBRCMPROC eglCreateGlobalImageBRCM; + PFNEGLDESTROYGLOBALIMAGEBRCMPROC eglDestroyGlobalImageBRCM; + + void *nativeResource(NativeResource resource) override; + void *nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) override; + +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); + + struct wl_display *m_waylandDisplay = nullptr; + struct qt_brcm *m_waylandBrcm = nullptr; + + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + + QWaylandDisplay *m_display = nullptr; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDBRCMEGLINTEGRATION_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.cpp new file mode 100644 index 00000000000..117e57807f6 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.cpp @@ -0,0 +1,230 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandbrcmeglwindow.h" + +#include <QtWaylandClient/private/qwaylandbuffer_p.h> +#include <QtWaylandClient/private/qwaylandscreen_p.h> +#include "qwaylandbrcmglcontext.h" + +#include <QtGui/private/qeglconvenience_p.h> + +#include <QtGui/QWindow> +#include <qpa/qwindowsysteminterface.h> + +#include <EGL/eglext_brcm.h> + +#include "wayland-brcm-client-protocol.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandBrcmBuffer : public QWaylandBuffer +{ +public: + QWaylandBrcmBuffer(QWaylandDisplay *display, + struct qt_brcm *brcm, + const QSize &size, + EGLint *data, + int count, + struct wl_event_queue *eventQueue) + : m_size(size) + , m_display(display) + , m_eventQueue(eventQueue) + { + wl_array_init(&m_array); + m_data = static_cast<EGLint *>(wl_array_add(&m_array, count * sizeof(EGLint))); + + for (int i = 0; i < count; ++i) + m_data[i] = data[i]; + + mBuffer = qt_brcm_create_buffer(brcm, size.width(), size.height(), &m_array); + wl_proxy_set_queue(reinterpret_cast<struct wl_proxy*>(mBuffer), m_eventQueue); + + static const struct wl_buffer_listener buffer_listener = { + QWaylandBrcmBuffer::buffer_release + }; + + wl_buffer_add_listener(mBuffer, &buffer_listener, this); + } + + ~QWaylandBrcmBuffer() + { + wl_array_release(&m_array); + wl_buffer_destroy(mBuffer); + mBuffer = nullptr; + } + + QSize size() const { return m_size; } + + void bind() + { + m_released = false; + } + + void waitForRelease() + { + if (m_released) + return; + while (!m_released) { + wl_display_dispatch_queue(m_display->wl_display(), m_eventQueue); + } + } + + static void buffer_release(void *data, wl_buffer *buffer) + { + Q_UNUSED(buffer); + static_cast<QWaylandBrcmBuffer *>(data)->m_released = true; + } + +private: + + QSize m_size; + bool m_released = true; + wl_array m_array; + EGLint *m_data = nullptr; + QWaylandDisplay *m_display = nullptr; + struct wl_event_queue *m_eventQueue = nullptr; +}; + +QWaylandBrcmEglWindow::QWaylandBrcmEglWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) + , m_eglIntegration(static_cast<QWaylandBrcmEglIntegration *>(mDisplay->clientBufferIntegration())) + , m_format(window->format()) + , m_eventQueue(wl_display_create_queue(mDisplay->wl_display())) +{ +} + +QWaylandBrcmEglWindow::~QWaylandBrcmEglWindow() +{ + destroyEglSurfaces(); +} + +QWaylandWindow::WindowType QWaylandBrcmEglWindow::windowType() const +{ + return QWaylandWindow::Egl; +} + +void QWaylandBrcmEglWindow::setGeometry(const QRect &rect) +{ + destroyEglSurfaces(); + QWaylandWindow::setGeometry(rect); +} + +QSurfaceFormat QWaylandBrcmEglWindow::format() const +{ + return m_format; +} + +void QWaylandBrcmEglWindow::destroyEglSurfaces() +{ + for (int i = 0; i < m_count; ++i) { + if (m_eglSurfaces[i]) { + eglDestroySurface(m_eglIntegration->eglDisplay(), m_eglSurfaces[i]); + m_eglSurfaces[i] = 0; + // the server does this + //m_eglIntegration->eglDestroyGlobalImageBRCM(&m_globalImages[5*i]); + delete m_buffers[i]; + } + } + + m_count = 0; + m_current = 0; +} + +QSurfaceFormat brcmFixFormat(const QSurfaceFormat &f) +{ + QSurfaceFormat format = f; + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + format.setAlphaBufferSize(8); + return format; +} + +void QWaylandBrcmEglWindow::createEglSurfaces() +{ + QSize size(geometry().size()); + + m_count = window()->format().swapBehavior() == QSurfaceFormat::TripleBuffer ? 3 : 2; + + EGLConfig eglConfig = q_configFromGLFormat(m_eglIntegration->eglDisplay(), brcmFixFormat(window()->format()), true, EGL_PIXMAP_BIT); + + m_format = q_glFormatFromConfig(m_eglIntegration->eglDisplay(), eglConfig); + + EGLint pixel_format = EGL_PIXEL_FORMAT_ARGB_8888_BRCM; + + EGLint rt; + eglGetConfigAttrib(m_eglIntegration->eglDisplay(), eglConfig, EGL_RENDERABLE_TYPE, &rt); + + if (rt & EGL_OPENGL_ES_BIT) { + pixel_format |= EGL_PIXEL_FORMAT_RENDER_GLES_BRCM; + pixel_format |= EGL_PIXEL_FORMAT_GLES_TEXTURE_BRCM; + } + + if (rt & EGL_OPENGL_ES2_BIT) { + pixel_format |= EGL_PIXEL_FORMAT_RENDER_GLES2_BRCM; + pixel_format |= EGL_PIXEL_FORMAT_GLES2_TEXTURE_BRCM; + } + + if (rt & EGL_OPENVG_BIT) { + pixel_format |= EGL_PIXEL_FORMAT_RENDER_VG_BRCM; + pixel_format |= EGL_PIXEL_FORMAT_VG_IMAGE_BRCM; + } + + if (rt & EGL_OPENGL_BIT) { + pixel_format |= EGL_PIXEL_FORMAT_RENDER_GL_BRCM; + } + + memset(m_globalImages, 0, 5 * m_count * sizeof(EGLint)); + for (int i = 0; i < m_count; ++i) { + m_eglIntegration->eglCreateGlobalImageBRCM(size.width(), size.height(), pixel_format, + 0, size.width() * 4, &m_globalImages[5*i]); + + m_globalImages[5*i+2] = size.width(); + m_globalImages[5*i+3] = size.height(); + m_globalImages[5*i+4] = pixel_format; + + EGLint attrs[] = { + EGL_VG_COLORSPACE, EGL_VG_COLORSPACE_sRGB, + EGL_VG_ALPHA_FORMAT, pixel_format & EGL_PIXEL_FORMAT_ARGB_8888_PRE_BRCM ? EGL_VG_ALPHA_FORMAT_PRE : EGL_VG_ALPHA_FORMAT_NONPRE, + EGL_NONE + }; + + m_eglSurfaces[i] = eglCreatePixmapSurface(m_eglIntegration->eglDisplay(), eglConfig, (EGLNativePixmapType)&m_globalImages[5*i], attrs); + if (m_eglSurfaces[i] == EGL_NO_SURFACE) + qFatal("eglCreatePixmapSurface failed: %x, global image id: %d %d\n", eglGetError(), m_globalImages[5*i], m_globalImages[5*i+1]); + m_buffers[i] = new QWaylandBrcmBuffer(mDisplay, m_eglIntegration->waylandBrcm(), size, &m_globalImages[5*i], 5, m_eventQueue); + } +} + +void QWaylandBrcmEglWindow::swapBuffers() +{ + if (m_eglIntegration->eglFlushBRCM) { + m_eglIntegration->eglFlushBRCM(); + } else { + glFlush(); + glFinish(); + } + + if (!m_count) + return; + + m_buffers[m_current]->bind(); + commit(m_buffers[m_current], QRegion(0, 0, geometry().size().width(), geometry().size().height())); + + m_current = (m_current + 1) % m_count; + m_buffers[m_current]->waitForRelease(); +} + +bool QWaylandBrcmEglWindow::makeCurrent(EGLContext context) +{ + if (!m_count) + const_cast<QWaylandBrcmEglWindow *>(this)->createEglSurfaces(); + return eglMakeCurrent(m_eglIntegration->eglDisplay(), m_eglSurfaces[m_current], m_eglSurfaces[m_current], context); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.h new file mode 100644 index 00000000000..3b9026f9c0a --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmeglwindow.h @@ -0,0 +1,59 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDBRCMEGLWINDOW_H +#define QWAYLANDBRCMEGLWINDOW_H + +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include "qwaylandbrcmeglintegration.h" + +#include <QMutex> + +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandBrcmBuffer; + +class QWaylandBrcmEglWindow : public QWaylandWindow +{ + Q_OBJECT +public: + QWaylandBrcmEglWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandBrcmEglWindow(); + WindowType windowType() const override; + void setGeometry(const QRect &rect) override; + + QSurfaceFormat format() const override; + + bool makeCurrent(EGLContext context); + void swapBuffers(); + +private: + void createEglSurfaces(); + void destroyEglSurfaces(); + + QWaylandBrcmEglIntegration *m_eglIntegration = nullptr; + struct wl_egl_window *m_waylandEglWindow = nullptr; + + const QWaylandWindow *m_parentWindow = nullptr; + + EGLint m_globalImages[3*5]; + EGLSurface m_eglSurfaces[3]; + + QWaylandBrcmBuffer *m_buffers[3]; + QSurfaceFormat m_format; + + struct wl_event_queue *m_eventQueue = nullptr; + + int m_current = 0; + int m_count = 0; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDBRCMEGLWINDOW_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.cpp new file mode 100644 index 00000000000..66135a7cc43 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.cpp @@ -0,0 +1,76 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandbrcmglcontext.h" + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include "qwaylandbrcmeglwindow.h" + +#include <QtGui/private/qeglconvenience_p.h> + +#include <qpa/qplatformopenglcontext.h> +#include <QtGui/QSurfaceFormat> + +#include <dlfcn.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +extern QSurfaceFormat brcmFixFormat(const QSurfaceFormat &format); + +QWaylandBrcmGLContext::QWaylandBrcmGLContext(EGLDisplay eglDisplay, const QSurfaceFormat &format, QPlatformOpenGLContext *share) + : QPlatformOpenGLContext() + , m_eglDisplay(eglDisplay) + , m_config(q_configFromGLFormat(m_eglDisplay, brcmFixFormat(format), true)) + , m_format(q_glFormatFromConfig(m_eglDisplay, m_config)) +{ + EGLContext shareEGLContext = share ? static_cast<QWaylandBrcmGLContext *>(share)->eglContext() : EGL_NO_CONTEXT; + + eglBindAPI(EGL_OPENGL_ES_API); + + QList<EGLint> eglContextAttrs; + eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION); + eglContextAttrs.append(format.majorVersion() == 1 ? 1 : 2); + eglContextAttrs.append(EGL_NONE); + + m_context = eglCreateContext(m_eglDisplay, m_config, shareEGLContext, eglContextAttrs.constData()); +} + +QWaylandBrcmGLContext::~QWaylandBrcmGLContext() +{ + eglDestroyContext(m_eglDisplay, m_context); +} + +bool QWaylandBrcmGLContext::makeCurrent(QPlatformSurface *surface) +{ + return static_cast<QWaylandBrcmEglWindow *>(surface)->makeCurrent(m_context); +} + +void QWaylandBrcmGLContext::doneCurrent() +{ + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +void QWaylandBrcmGLContext::swapBuffers(QPlatformSurface *surface) +{ + static_cast<QWaylandBrcmEglWindow *>(surface)->swapBuffers(); +} + +QFunctionPointer QWaylandBrcmGLContext::getProcAddress(const char *procName) +{ + QFunctionPointer proc = (QFunctionPointer) eglGetProcAddress(procName); + if (!proc) + proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName); + return proc; +} + +EGLConfig QWaylandBrcmGLContext::eglConfig() const +{ + return m_config; +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.h new file mode 100644 index 00000000000..e64a20d9b73 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/brcm-egl/qwaylandbrcmglcontext.h @@ -0,0 +1,48 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDBRCMGLCONTEXT_H +#define QWAYLANDBRCMGLCONTEXT_H + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> + +#include <qpa/qplatformopenglcontext.h> + +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandWindow; + +class QWaylandBrcmGLContext : public QPlatformOpenGLContext { +public: + QWaylandBrcmGLContext(EGLDisplay eglDisplay, const QSurfaceFormat &format, QPlatformOpenGLContext *share); + ~QWaylandBrcmGLContext(); + + void swapBuffers(QPlatformSurface *surface) override; + + bool makeCurrent(QPlatformSurface *surface) override; + void doneCurrent() override; + + QFunctionPointer getProcAddress(const char *procName) override; + + QSurfaceFormat format() const override { return m_format; } + + EGLConfig eglConfig() const; + EGLContext eglContext() const { return m_context; } + +private: + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + + EGLContext m_context; + EGLConfig m_config; + QSurfaceFormat m_format; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDBRCMGLCONTEXT_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/CMakeLists.txt index 7329aebc323..68c09f76e30 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/CMakeLists.txt @@ -11,10 +11,8 @@ qt_internal_add_plugin(DmaBufServerBufferPlugin OUTPUT_NAME dmabuf-server PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/dmabuf-server/dmabufserverbufferintegration.cpp ../../../../hardwareintegration/client/dmabuf-server/dmabufserverbufferintegration.h + dmabufserverbufferintegration.cpp dmabufserverbufferintegration.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/dmabuf-server LIBRARIES EGL::EGL Qt::Core diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.cpp new file mode 100644 index 00000000000..54df2badd2e --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "dmabufserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtOpenGL/QOpenGLTexture> +#include <QtGui/QOpenGLContext> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +DmaBufServerBuffer::DmaBufServerBuffer(DmaBufServerBufferIntegration *integration + , struct ::qt_server_buffer *id + , int32_t fd + , int32_t width + , int32_t height + , int32_t stride + , int32_t offset + , int32_t fourcc_format) + : m_integration(integration) + , m_server_buffer(id) +{ + m_size = QSize(width, height); + + + EGLint import_attribs[] = { + EGL_WIDTH, width, + EGL_HEIGHT, height, + EGL_LINUX_DRM_FOURCC_EXT, fourcc_format, + EGL_DMA_BUF_PLANE0_FD_EXT, fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, offset, + EGL_DMA_BUF_PLANE0_PITCH_EXT, stride, + EGL_NONE + }; + + m_image = integration->eglCreateImageKHR( + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer)nullptr, + import_attribs); + + int err = eglGetError(); + qCDebug(lcQpaWayland) << "imported egl image" << m_image; + if (m_image == EGL_NO_IMAGE_KHR || err != EGL_SUCCESS) + qCWarning(lcQpaWayland) << "DmaBufServerBuffer error importing image. EGL error code" << Qt::hex << err; + + qt_server_buffer_set_user_data(id, this); + +} + +DmaBufServerBuffer::~DmaBufServerBuffer() +{ + int err = m_integration->eglDestroyImageKHR(m_image); + if (err != EGL_SUCCESS) + qCWarning(lcQpaWayland) << "~DmaBufServerBuffer error destroying image" << m_image << "error code " << Qt::hex << err; + qt_server_buffer_release(m_server_buffer); + qt_server_buffer_destroy(m_server_buffer); +} + +QOpenGLTexture *DmaBufServerBuffer::toOpenGlTexture() +{ + if (!QOpenGLContext::currentContext()) + qCWarning(lcQpaWayland) <<"DmaBufServerBuffer: creating texture with no current context"; + + if (!m_texture) { + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + } + + m_texture->bind(); + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); + return m_texture; +} + +void DmaBufServerBufferIntegration::initializeEgl() +{ + if (m_egl_initialized) + return; + m_egl_initialized = true; + + m_egl_display = eglGetDisplay((EGLNativeDisplayType) m_display->wl_display()); + if (m_egl_display == EGL_NO_DISPLAY) { + qCWarning(lcQpaWayland) << "Failed to initialize drm egl server buffer integration. Could not get egl display from wl_display."; + return; + } + + const char *extensionString = eglQueryString(m_egl_display, EGL_EXTENSIONS); + + + if (!extensionString || !strstr(extensionString, "EGL_KHR_image")) { + qCWarning(lcQpaWayland) << "Failed to initialize dmabuf server buffer integration. There is no EGL_KHR_image extension.\n"; + return; + } + m_egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + m_egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + if (!m_egl_create_image || !m_egl_destroy_image) { + qCWarning(lcQpaWayland) << "Failed to initialize dmabuf server buffer integration. Could not resolve eglCreateImageKHR or eglDestroyImageKHR"; + return; + } + + m_gl_egl_image_target_texture = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + if (!m_gl_egl_image_target_texture) { + qCWarning(lcQpaWayland) << "Failed to initialize dmabuf server buffer integration. Could not resolve glEGLImageTargetTexture2DOES"; + return; + } +} + +void DmaBufServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *DmaBufServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void DmaBufServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == QStringLiteral("qt_dmabuf_server_buffer")) { + auto *integration = static_cast<DmaBufServerBufferIntegration *>(data); + integration->QtWayland::qt_dmabuf_server_buffer::init(registry, id, 1); + } +} + +void DmaBufServerBufferIntegration::dmabuf_server_buffer_server_buffer_created(struct ::qt_server_buffer *id + , int32_t name + , int32_t width + , int32_t height + , int32_t stride + , int32_t offset + , int32_t format) +{ + (void) new DmaBufServerBuffer(this, id, name, width, height, stride, offset, format); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.h new file mode 100644 index 00000000000..13dbdfbfbab --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/dmabuf-server/dmabufserverbufferintegration.h @@ -0,0 +1,111 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef DMABUFSERVERBUFFERINTEGRATION_H +#define DMABUFSERVERBUFFERINTEGRATION_H + +#include <QtCore/QVariant> +#include <QtWaylandClient/private/qwayland-wayland.h> +#include "qwayland-qt-dmabuf-server-buffer.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> + +#include "dmabufserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QTextStream> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#ifndef EGL_KHR_image +typedef void *EGLImageKHR; +typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); +#endif + +#ifndef GL_OES_EGL_image +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class DmaBufServerBufferIntegration; + +class DmaBufServerBuffer : public QWaylandServerBuffer +{ +public: + DmaBufServerBuffer(DmaBufServerBufferIntegration *integration, struct ::qt_server_buffer *id, int32_t fd, + int32_t width, int32_t height, int32_t stride, int32_t offset, int32_t fourcc_format); + ~DmaBufServerBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; +private: + DmaBufServerBufferIntegration *m_integration = nullptr; + EGLImageKHR m_image = EGL_NO_IMAGE_KHR; + QOpenGLTexture *m_texture = nullptr; + struct ::qt_server_buffer *m_server_buffer = nullptr; +}; + +class DmaBufServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::qt_dmabuf_server_buffer +{ +public: + void initialize(QWaylandDisplay *display) override; + + QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + + inline EGLImageKHR eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); + inline EGLBoolean eglDestroyImageKHR(EGLImageKHR image); + inline void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image); +protected: + void dmabuf_server_buffer_server_buffer_created(struct ::qt_server_buffer *id, int32_t fd, + int32_t width, int32_t height, int32_t stride, + int32_t offset, int32_t fourcc_format) override; + +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + void initializeEgl(); + + PFNEGLCREATEIMAGEKHRPROC m_egl_create_image = nullptr; + PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image = nullptr; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture = nullptr; + QWaylandDisplay *m_display = nullptr; + EGLDisplay m_egl_display = EGL_NO_DISPLAY; + bool m_egl_initialized = false; +}; + +EGLImageKHR DmaBufServerBufferIntegration::eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) +{ + if (!m_egl_initialized) + initializeEgl(); + if (!m_egl_create_image) { + qCWarning(lcQpaWayland) << "DmaBufServerBufferIntegration: Trying to use unresolved function eglCreateImageKHR"; + return EGL_NO_IMAGE_KHR; + } + return m_egl_create_image(m_egl_display, ctx, target, buffer, attrib_list); +} + +EGLBoolean DmaBufServerBufferIntegration::eglDestroyImageKHR(EGLImageKHR image) +{ + if (!m_egl_destroy_image) { + qCWarning(lcQpaWayland) << "DmaBufServerBufferIntegration: Trying to use unresolved function eglDestroyImageKHR"; + return false; + } + return m_egl_destroy_image(m_egl_display, image); +} + +void DmaBufServerBufferIntegration::glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) +{ + if (!m_gl_egl_image_target_texture) { + qCWarning(lcQpaWayland) << "DmaBufServerBufferIntegration: Trying to use unresolved function glEGLImageTargetTexture2DOES"; + return; + } + m_gl_egl_image_target_texture(target, image); +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/CMakeLists.txt index 535b1024f0f..c552a9b2878 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/CMakeLists.txt @@ -11,10 +11,8 @@ qt_internal_add_plugin(DrmEglServerBufferPlugin OUTPUT_NAME drm-egl-server PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.cpp ../../../../hardwareintegration/client/drm-egl-server/drmeglserverbufferintegration.h + drmeglserverbufferintegration.cpp drmeglserverbufferintegration.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/drm-egl-server LIBRARIES EGL::EGL Qt::Core diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.cpp new file mode 100644 index 00000000000..16a3aa4cf51 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "drmeglserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtOpenGL/QOpenGLTexture> +#include <QtGui/QOpenGLContext> + +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +DrmServerBuffer::DrmServerBuffer(DrmEglServerBufferIntegration *integration + , int32_t name + , int32_t width + , int32_t height + , int32_t stride + , int32_t format) + : m_integration(integration) +{ + m_size = QSize(width, height); + EGLint egl_format; + int32_t format_stride; + switch (format) { + case QtWayland::qt_drm_egl_server_buffer::format_RGBA32: + m_format = QWaylandServerBuffer::RGBA32; + egl_format = EGL_DRM_BUFFER_FORMAT_ARGB32_MESA; + format_stride = stride / 4; + break; +#ifdef EGL_DRM_BUFFER_FORMAT_A8_MESA + case QtWayland::qt_drm_egl_server_buffer::format_A8: + m_format = QWaylandServerBuffer::A8; + egl_format = EGL_DRM_BUFFER_FORMAT_A8_MESA; + format_stride = stride; + break; +#endif + default: + qWarning("DrmServerBuffer: unknown format"); + m_format = QWaylandServerBuffer::RGBA32; + egl_format = EGL_DRM_BUFFER_FORMAT_ARGB32_MESA; + format_stride = stride / 4; + break; + } + EGLint attribs[] = { + EGL_WIDTH, width, + EGL_HEIGHT, height, + EGL_DRM_BUFFER_STRIDE_MESA, format_stride, + EGL_DRM_BUFFER_FORMAT_MESA, egl_format, + EGL_NONE + }; + + qintptr name_pointer = name; + m_image = integration->eglCreateImageKHR(EGL_NO_CONTEXT, EGL_DRM_BUFFER_MESA, (EGLClientBuffer) name_pointer, attribs); + +} + +DrmServerBuffer::~DrmServerBuffer() +{ + m_integration->eglDestroyImageKHR(m_image); +} + +QOpenGLTexture *DrmServerBuffer::toOpenGlTexture() +{ + if (!QOpenGLContext::currentContext()) + qWarning("DrmServerBuffer: creating texture with no current context"); + + if (!m_texture) { + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + } + + m_texture->bind(); + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); + return m_texture; +} + +void DrmEglServerBufferIntegration::initializeEgl() +{ + if (m_egl_initialized) + return; + m_egl_initialized = true; + +#if QT_CONFIG(egl_extension_platform_wayland) + m_egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, m_display->wl_display(), nullptr); +#else + m_egl_display = eglGetDisplay((EGLNativeDisplayType) m_display->wl_display()); +#endif + if (m_egl_display == EGL_NO_DISPLAY) { + qWarning("Failed to initialize drm egl server buffer integration. Could not get egl display from wl_display."); + return; + } + + const char *extensionString = eglQueryString(m_egl_display, EGL_EXTENSIONS); + if (!extensionString || !strstr(extensionString, "EGL_KHR_image")) { + qWarning("Failed to initialize drm egl server buffer integration. There is no EGL_KHR_image extension.\n"); + return; + } + m_egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + m_egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + if (!m_egl_create_image || !m_egl_destroy_image) { + qWarning("Failed to initialize drm egl server buffer integration. Could not resolve eglCreateImageKHR or eglDestroyImageKHR"); + return; + } + + m_gl_egl_image_target_texture = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + if (!m_gl_egl_image_target_texture) { + qWarning("Failed to initialize drm egl server buffer integration. Could not resolve glEGLImageTargetTexture2DOES"); + return; + } + m_egl_initialized = true; +} + +void DrmEglServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *DrmEglServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void DrmEglServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == QStringLiteral("qt_drm_egl_server_buffer")) { + auto *integration = static_cast<DrmEglServerBufferIntegration *>(data); + integration->QtWayland::qt_drm_egl_server_buffer::init(registry, id, 1); + } +} + +void DrmEglServerBufferIntegration::drm_egl_server_buffer_server_buffer_created(struct ::qt_server_buffer *id + , int32_t name + , int32_t width + , int32_t height + , int32_t stride + , int32_t format) +{ + DrmServerBuffer *server_buffer = new DrmServerBuffer(this, name, width, height, stride, format); + qt_server_buffer_set_user_data(id, server_buffer); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.h new file mode 100644 index 00000000000..a06bc96b87d --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/drm-egl-server/drmeglserverbufferintegration.h @@ -0,0 +1,106 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef DRMEGLSERVERBUFFERINTEGRATION_H +#define DRMEGLSERVERBUFFERINTEGRATION_H + +#include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtCore/QVariant> +#include "qwayland-drm-egl-server-buffer.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> + +#include "drmeglserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QTextStream> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#ifndef EGL_KHR_image +typedef void *EGLImageKHR; +typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); +#endif + +#ifndef GL_OES_EGL_image +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class DrmEglServerBufferIntegration; + +class DrmServerBuffer : public QWaylandServerBuffer +{ +public: + DrmServerBuffer(DrmEglServerBufferIntegration *integration, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format); + ~DrmServerBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; +private: + DrmEglServerBufferIntegration *m_integration = nullptr; + EGLImageKHR m_image; + QOpenGLTexture *m_texture = nullptr; +}; + +class DrmEglServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::qt_drm_egl_server_buffer +{ +public: + void initialize(QWaylandDisplay *display) override; + + QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + + inline EGLImageKHR eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); + inline EGLBoolean eglDestroyImageKHR (EGLImageKHR image); + inline void glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); +protected: + void drm_egl_server_buffer_server_buffer_created(struct ::qt_server_buffer *id, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format) override; +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + void initializeEgl(); + + PFNEGLCREATEIMAGEKHRPROC m_egl_create_image; + PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture; + QWaylandDisplay *m_display = nullptr; + EGLDisplay m_egl_display = EGL_NO_DISPLAY; + bool m_egl_initialized = false; +}; + +EGLImageKHR DrmEglServerBufferIntegration::eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) +{ + if (!m_egl_initialized) + initializeEgl(); + if (!m_egl_create_image) { + qWarning("DrmEglServerBufferIntegration: Trying to used unresolved function eglCreateImageKHR"); + return EGL_NO_IMAGE_KHR; + } + return m_egl_create_image(m_egl_display, ctx, target, buffer,attrib_list); +} + +EGLBoolean DrmEglServerBufferIntegration::eglDestroyImageKHR (EGLImageKHR image) +{ + if (!m_egl_destroy_image) { + qWarning("DrmEglServerBufferIntegration: Trying to use unresolved function eglDestroyImageKHR"); + return false; + } + return m_egl_destroy_image(m_egl_display, image); +} + +void DrmEglServerBufferIntegration::glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image) +{ + if (!m_gl_egl_image_target_texture) { + qWarning("DrmEglServerBufferIntegration: Trying to use unresolved function glEGLImageTargetRenderbufferStorageOES"); + return; + } + m_gl_egl_image_target_texture(target,image); +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/CMakeLists.txt index 6b7cd897e19..351f8f6d3f4 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/CMakeLists.txt @@ -11,10 +11,8 @@ qt_internal_add_plugin(LibHybrisEglServerBufferPlugin OUTPUT_NAME libhybris-egl-server PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.cpp ../../../../hardwareintegration/client/libhybris-egl-server/libhybriseglserverbufferintegration.h + libhybriseglserverbufferintegration.cpp libhybriseglserverbufferintegration.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/libhybris-egl-server PUBLIC_LIBRARIES EGL::EGL Qt::Core @@ -27,7 +25,7 @@ qt_internal_add_plugin(LibHybrisEglServerBufferPlugin qt6_generate_wayland_protocol_client_sources(LibHybrisEglServerBufferPlugin PRIVATE_CODE FILES - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../hardwareintegration/client/libhybris-egl-server/../../../extensions/libhybris-egl-server-buffer.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../../../extensions/libhybris-egl-server-buffer.xml ) #### Keys ignored in scope 1:.:.:libhybris-egl-server.pro:<TRUE>: diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.cpp new file mode 100644 index 00000000000..54081de97af --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2016 Jolla Ltd, author: <[email protected]> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "libhybriseglserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <hybris/eglplatformcommon/hybris_nativebufferext.h> + +#include <EGL/egl.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +LibHybrisServerBuffer::LibHybrisServerBuffer(LibHybrisEglServerBufferIntegration *integration + , int32_t numFds + , wl_array *ints + , int32_t name + , int32_t width + , int32_t height + , int32_t stride + , int32_t format) + : QWaylandServerBuffer() + , m_integration(integration) + , m_stride(stride) + , m_hybrisFormat(format) +{ + m_numFds = numFds; + m_fds.reserve(numFds); + m_ints.resize(ints->size / sizeof(int32_t)); + memcpy(m_ints.data(), ints->data, ints->size); + m_image = 0; + + m_size = QSize(width, height); +} + +LibHybrisServerBuffer::~LibHybrisServerBuffer() +{ + m_integration->eglDestroyImageKHR(m_image); +} + +QOpenGLTexture * LibHybrisServerBuffer::toOpenGlTexture() +{ + if (!QOpenGLContext::currentContext()) { + qWarning("LibHybrisServerBuffer: creating texture with no current context"); + } + + if (!m_texture) { + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + } + + m_texture->bind(); + m_integration->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + m_texture->release(); + m_texture->setSize(m_size.width(), m_size.height()); + + return m_texture; +} + +void LibHybrisServerBuffer::libhybris_buffer_add_fd(int32_t fd) +{ + m_fds << fd; + + if (m_fds.size() == m_numFds) { + EGLint egl_format; + switch (m_hybrisFormat) { + case QtWayland::qt_libhybris_egl_server_buffer::format_RGBA32: + m_format = QWaylandServerBuffer::RGBA32; + egl_format = HYBRIS_PIXEL_FORMAT_RGBA_8888; + break; + default: + qWarning("LibHybrisServerBuffer: unknown format"); + m_format = QWaylandServerBuffer::RGBA32; + egl_format = HYBRIS_PIXEL_FORMAT_RGBA_8888; + break; + } + + EGLClientBuffer buf; + m_integration->eglHybrisCreateRemoteBuffer(m_size.width(), m_size.height(), HYBRIS_USAGE_HW_TEXTURE, egl_format, m_stride, m_ints.size(), m_ints.data(), m_fds.size(), m_fds.data(), &buf); + m_image = m_integration->eglCreateImageKHR(EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, buf, 0); + } +} + +void LibHybrisEglServerBufferIntegration::initializeEgl() +{ + if (m_egl_initialized) + return; + m_egl_initialized = true; + + m_egl_display = eglGetDisplay((EGLNativeDisplayType) m_display->wl_display()); + if (m_egl_display == EGL_NO_DISPLAY) { + qWarning("Failed to initialize libhybris egl server buffer integration. Could not get egl display from wl_display."); + return; + } + + const char *extensionString = eglQueryString(m_egl_display, EGL_EXTENSIONS); + if (!extensionString || !strstr(extensionString, "EGL_KHR_image")) { + qWarning("Failed to initialize libhybris egl server buffer integration. There is no EGL_KHR_image extension.\n"); + return; + } + m_egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + m_egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + if (!m_egl_create_image || !m_egl_destroy_image) { + qWarning("Failed to initialize libhybris egl server buffer integration. Could not resolve eglCreateImageKHR or eglDestroyImageKHR"); + return; + } + + m_gl_egl_image_target_texture = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + if (!m_gl_egl_image_target_texture) { + qWarning("Failed to initialize libhybris egl server buffer integration. Could not resolve glEGLImageTargetTexture2DOES"); + return; + } + + m_egl_create_buffer = reinterpret_cast<PFNEGLHYBRISCREATEREMOTEBUFFERPROC>(eglGetProcAddress("eglHybrisCreateRemoteBuffer")); + if (!m_egl_create_buffer) { + qWarning("Failed to initialize libhybris egl server buffer integration. Could not resolve eglHybrisCreateRemoteBuffer"); + return; + } + m_egl_initialized = true; +} + +void LibHybrisEglServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *LibHybrisEglServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void LibHybrisEglServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == QStringLiteral("qt_libhybris_egl_server_buffer")) { + auto *integration = static_cast<LibHybrisEglServerBufferIntegration *>(data); + integration->QtWayland::qt_libhybris_egl_server_buffer::init(registry, id, 1); + } +} + +void LibHybrisEglServerBufferIntegration::libhybris_egl_server_buffer_server_buffer_created(struct ::qt_libhybris_buffer *id + , struct ::qt_server_buffer *qid + , int32_t numFds + , wl_array *ints + , int32_t name + , int32_t width + , int32_t height + , int32_t stride + , int32_t format) +{ + LibHybrisServerBuffer *server_buffer = new LibHybrisServerBuffer(this, numFds, ints, name, width, height, stride, format); + server_buffer->QtWayland::qt_libhybris_buffer::init(id); + qt_server_buffer_set_user_data(qid, server_buffer); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.h new file mode 100644 index 00000000000..7fd4a2abbf8 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/libhybris-egl-server/libhybriseglserverbufferintegration.h @@ -0,0 +1,133 @@ +// Copyright (C) 2016 Jolla Ltd, author: <[email protected]> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef LIBHYBRISEGLSERVERBUFFERINTEGRATION_H +#define LIBHYBRISEGLSERVERBUFFERINTEGRATION_H + +#include <QtWaylandClient/private/qwayland-wayland.h> +#include "qwayland-libhybris-egl-server-buffer.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QList> +#include <QtCore/QTextStream> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#ifndef EGL_KHR_image +typedef void *EGLImageKHR; +typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); +#endif + +#ifndef GL_OES_EGL_image +typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); +#endif + +#ifndef EGL_HYBRIS_native_buffer +typedef EGLBoolean (EGLAPIENTRYP PFNEGLHYBRISCREATEREMOTEBUFFERPROC)(EGLint width, EGLint height, EGLint usage, EGLint format, EGLint stride, + int num_ints, int *ints, int num_fds, int *fds, EGLClientBuffer *buffer); +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class LibHybrisEglServerBufferIntegration; + +class LibHybrisServerBuffer : public QWaylandServerBuffer, public QtWayland::qt_libhybris_buffer +{ +public: + LibHybrisServerBuffer(LibHybrisEglServerBufferIntegration *integration, int32_t numFds, wl_array *ints, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format); + ~LibHybrisServerBuffer(); + QOpenGLTexture* toOpenGlTexture() override; + +protected: + void libhybris_buffer_add_fd(int32_t fd) override; + +private: + LibHybrisEglServerBufferIntegration *m_integration = nullptr; + EGLImageKHR m_image; + QOpenGLTexture *m_texture = nullptr; + int m_numFds; + QList<int32_t> m_ints; + QList<int32_t> m_fds; + int32_t m_stride; + int32_t m_hybrisFormat; +}; + +class LibHybrisEglServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::qt_libhybris_egl_server_buffer +{ +public: + void initialize(QWaylandDisplay *display) override; + + virtual QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + + inline EGLImageKHR eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); + inline EGLBoolean eglDestroyImageKHR (EGLImageKHR image); + inline void glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image); + inline EGLBoolean eglHybrisCreateRemoteBuffer(EGLint width, EGLint height, EGLint usage, EGLint format, EGLint stride, int num_ints, int *ints, int num_fds, int *fds, EGLClientBuffer *buffer); + +protected: + void libhybris_egl_server_buffer_server_buffer_created(struct ::qt_libhybris_buffer *id, struct ::qt_server_buffer *qid, + int32_t numFds, wl_array *ints, int32_t name, int32_t width, int32_t height, int32_t stride, int32_t format) override; +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + void initializeEgl(); + + PFNEGLCREATEIMAGEKHRPROC m_egl_create_image; + PFNEGLDESTROYIMAGEKHRPROC m_egl_destroy_image; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_gl_egl_image_target_texture; + PFNEGLHYBRISCREATEREMOTEBUFFERPROC m_egl_create_buffer; + QWaylandDisplay *m_display = nullptr; + EGLDisplay m_egl_display = EGL_NO_DISPLAY; + bool m_egl_initialized = false; +}; + +EGLImageKHR LibHybrisEglServerBufferIntegration::eglCreateImageKHR(EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) +{ + if (!m_egl_initialized) + initializeEgl(); + + if (!m_egl_create_image) { + qWarning("LibHybrisEglServerBufferIntegration: Trying to used unresolved function eglCreateImageKHR"); + return EGL_NO_IMAGE_KHR; + } + return m_egl_create_image(m_egl_display, ctx, target, buffer,attrib_list); +} + +EGLBoolean LibHybrisEglServerBufferIntegration::eglDestroyImageKHR (EGLImageKHR image) +{ + if (!m_egl_destroy_image) { + qWarning("LibHybrisEglServerBufferIntegration: Trying to use unresolved function eglDestroyImageKHR"); + return false; + } + return m_egl_destroy_image(m_egl_display, image); +} + +void LibHybrisEglServerBufferIntegration::glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image) +{ + if (!m_gl_egl_image_target_texture) { + qWarning("LibHybrisEglServerBufferIntegration: Trying to use unresolved function glEGLImageTargetRenderbufferStorageOES"); + return; + } + m_gl_egl_image_target_texture(target,image); +} + +EGLBoolean LibHybrisEglServerBufferIntegration::eglHybrisCreateRemoteBuffer(EGLint width, EGLint height, EGLint usage, EGLint format, EGLint stride, + int num_ints, int *ints, int num_fds, int *fds, EGLClientBuffer *buffer) +{ + if (!m_egl_create_buffer) { + qWarning("LibHybrisEglServerBufferIntegration: Trying to use unresolved function eglHybrisCreateRemoteBuffer"); + return false; + } + return m_egl_create_buffer(width, height, usage, format, stride, num_ints, ints, num_fds, fds, buffer); +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/CMakeLists.txt index b979086d2c7..8f20ada4cb4 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/CMakeLists.txt @@ -11,10 +11,8 @@ qt_internal_add_plugin(ShmServerBufferPlugin OUTPUT_NAME shm-emulation-server PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.cpp ../../../../hardwareintegration/client/shm-emulation-server/shmserverbufferintegration.h + shmserverbufferintegration.cpp shmserverbufferintegration.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/shm-emulation-server LIBRARIES Qt::Core Qt::Gui diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.cpp new file mode 100644 index 00000000000..38065dbde16 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.cpp @@ -0,0 +1,107 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "shmserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtOpenGL/QOpenGLTexture> +#include <QtGui/QOpenGLContext> +#include <QtGui/QImage> +#include <QtCore/QSharedMemory> + +QT_BEGIN_NAMESPACE + +static QOpenGLTexture *createTextureFromShm(const QString &key, int w, int h, int bpl, int format) +{ + QT_IGNORE_DEPRECATIONS(QSharedMemory shm(key);) + bool ok; + ok = shm.attach(QSharedMemory::ReadOnly); + if (!ok) { + qWarning() << "Could not attach to" << key; + return nullptr; + } + ok = shm.lock(); + if (!ok) { + qWarning() << "Could not lock" << key << "for reading"; + return nullptr; + } + + QImage::Format imageFormat; + switch (format) { + case QtWayland::qt_shm_emulation_server_buffer::format_RGBA32: + imageFormat = QImage::Format_RGBA8888; + break; + case QtWayland::qt_shm_emulation_server_buffer::format_A8: + imageFormat = QImage::Format_Alpha8; + break; + default: + qWarning() << "ShmServerBuffer: unknown format" << format; + imageFormat = QImage::Format_RGBA8888; + break; + } + + QImage image(static_cast<const uchar*>(shm.constData()), w, h, bpl, imageFormat); + + if (!QOpenGLContext::currentContext()) + qWarning("ShmServerBuffer: creating texture with no current context"); + + auto *tex = new QOpenGLTexture(image, QOpenGLTexture::DontGenerateMipMaps); + shm.unlock(); + return tex; +} + + +namespace QtWaylandClient { + +ShmServerBuffer::ShmServerBuffer(const QString &key, const QSize& size, int bytesPerLine, QWaylandServerBuffer::Format format) + : m_key(key) + , m_bpl(bytesPerLine) +{ + m_format = format; + m_size = size; +} + +ShmServerBuffer::~ShmServerBuffer() +{ +} + +QOpenGLTexture *ShmServerBuffer::toOpenGlTexture() +{ + if (!m_texture) + m_texture = createTextureFromShm(m_key, m_size.width(), m_size.height(), m_bpl, m_format); + + return m_texture; +} + +void ShmServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *ShmServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void ShmServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == "qt_shm_emulation_server_buffer") { + auto *integration = static_cast<ShmServerBufferIntegration *>(data); + integration->QtWayland::qt_shm_emulation_server_buffer::init(registry, id, 1); + } +} + + +void QtWaylandClient::ShmServerBufferIntegration::shm_emulation_server_buffer_server_buffer_created(qt_server_buffer *id, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format) +{ + QSize size(width, height); + auto fmt = QWaylandServerBuffer::Format(format); + auto *server_buffer = new ShmServerBuffer(key, size, bytes_per_line, fmt); + qt_server_buffer_set_user_data(id, server_buffer); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.h new file mode 100644 index 00000000000..344046ae182 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/shm-emulation-server/shmserverbufferintegration.h @@ -0,0 +1,55 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef SHMSERVERBUFFERINTEGRATION_H +#define SHMSERVERBUFFERINTEGRATION_H + +#include <QtWaylandClient/private/qwayland-wayland.h> +#include "qwayland-shm-emulation-server-buffer.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> + +#include "shmserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QTextStream> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class ShmServerBufferIntegration; + +class ShmServerBuffer : public QWaylandServerBuffer +{ +public: + ShmServerBuffer(const QString &key, const QSize &size, int bytesPerLine, QWaylandServerBuffer::Format format); + ~ShmServerBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; +private: + QOpenGLTexture *m_texture = nullptr; + QString m_key; + int m_bpl; +}; + +class ShmServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::qt_shm_emulation_server_buffer +{ +public: + void initialize(QWaylandDisplay *display) override; + + QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + +protected: + void shm_emulation_server_buffer_server_buffer_created(qt_server_buffer *id, const QString &key, int32_t width, int32_t height, int32_t bytes_per_line, int32_t format) override; + +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + QWaylandDisplay *m_display = nullptr; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/CMakeLists.txt index f0aeabf291a..34ce655330b 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/CMakeLists.txt @@ -11,10 +11,8 @@ qt_internal_add_plugin(VulkanServerBufferPlugin OUTPUT_NAME vulkan-server PLUGIN_TYPE wayland-graphics-integration-client SOURCES - ../../../../hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp ../../../../hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h + vulkanserverbufferintegration.cpp vulkanserverbufferintegration.h main.cpp - INCLUDE_DIRECTORIES - ../../../../hardwareintegration/client/vulkan-server LIBRARIES Qt::Core Qt::Gui diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.cpp new file mode 100644 index 00000000000..8f1ff9a46d3 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "vulkanserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtOpenGL/QOpenGLTexture> +#include <QtGui/QOpenGLContext> +#include <QtGui/qopengl.h> +#include <QtGui/QImage> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +static constexpr bool sbiExtraDebug = +#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG + true; +#else + false; +#endif + +#define DECL_GL_FUNCTION(name, type) \ + type name + +#define FIND_GL_FUNCTION(name, type) \ + do { \ + name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \ + if (!name) { \ + qWarning() << "ERROR in GL proc lookup. Could not find " #name; \ + return false; \ + } \ + } while (0) + +struct VulkanServerBufferGlFunctions +{ + DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC); + DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC); + DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC); + DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC); + DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC); + + bool init(QOpenGLContext *glContext) + { + FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC); + FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC); + FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC); + FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC); + FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC); + + return true; + } + static bool create(QOpenGLContext *glContext); +}; + +static VulkanServerBufferGlFunctions *funcs = nullptr; + +bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext) +{ + if (funcs) + return true; + funcs = new VulkanServerBufferGlFunctions; + if (!funcs->init(glContext)) { + delete funcs; + funcs = nullptr; + return false; + } + return true; +} + +VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id, + int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) + : m_integration(integration) + , m_server_buffer(id) + , m_fd(fd) + , m_memorySize(memory_size) + , m_internalFormat(format) +{ + m_size = QSize(width, height); +} + +VulkanServerBuffer::~VulkanServerBuffer() +{ + if (QCoreApplication::closingDown()) + return; // can't trust anything at this point + + if (m_texture) { //only do gl cleanup if import has been called + m_integration->deleteGLTextureWhenPossible(m_texture); + + if (sbiExtraDebug) qDebug() << "glDeleteMemoryObjectsEXT" << m_memoryObject; + funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject); + } + qt_server_buffer_release(m_server_buffer); + qt_server_buffer_destroy(m_server_buffer); +} + +void VulkanServerBuffer::import() +{ + if (m_texture) + return; + + if (sbiExtraDebug) qDebug() << "importing" << m_fd << Qt::hex << glGetError(); + + auto *glContext = QOpenGLContext::currentContext(); + if (!glContext) + return; + + if (!funcs && !VulkanServerBufferGlFunctions::create(glContext)) + return; + + funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject); + if (sbiExtraDebug) qDebug() << "glCreateMemoryObjectsEXT" << Qt::hex << glGetError(); + funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, m_fd); + if (sbiExtraDebug) qDebug() << "glImportMemoryFdEXT" << Qt::hex << glGetError(); + + + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + + if (sbiExtraDebug) qDebug() << "created texture" << m_texture->textureId() << Qt::hex << glGetError(); + + m_texture->bind(); + if (sbiExtraDebug) qDebug() << "bound texture" << Qt::hex << glGetError(); + funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_internalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 ); + if (sbiExtraDebug) qDebug() << "glTexStorageMem2DEXT" << Qt::hex << glGetError(); + if (sbiExtraDebug) qDebug() << "format" << Qt::hex << m_internalFormat << GL_RGBA8; +} + +QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture() +{ + m_integration->deleteOrphanedTextures(); + if (!m_texture) + import(); + return m_texture; +} + +void VulkanServerBufferIntegration::initialize(QWaylandDisplay *display) +{ + m_display = display; + display->addRegistryListener(&wlDisplayHandleGlobal, this); +} + +QWaylandServerBuffer *VulkanServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) +{ + return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); +} + +void VulkanServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +{ + Q_UNUSED(version); + if (interface == "zqt_vulkan_server_buffer_v1") { + auto *integration = static_cast<VulkanServerBufferIntegration *>(data); + integration->QtWayland::zqt_vulkan_server_buffer_v1::init(registry, id, 1); + } +} + +void VulkanServerBufferIntegration::zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) +{ + if (sbiExtraDebug) qDebug() << "vulkan_server_buffer_server_buffer_created" << fd; + auto *server_buffer = new VulkanServerBuffer(this, id, fd, width, height, memory_size, format); + qt_server_buffer_set_user_data(id, server_buffer); +} + +void VulkanServerBufferIntegration::deleteOrphanedTextures() +{ + if (!QOpenGLContext::currentContext()) { + qWarning("VulkanServerBufferIntegration::deleteOrphanedTextures with no current context!"); + return; + } + qDeleteAll(orphanedTextures); + orphanedTextures.clear(); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.h new file mode 100644 index 00000000000..2f0867a8197 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/vulkan-server/vulkanserverbufferintegration.h @@ -0,0 +1,66 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef VULKANSERVERBUFFERINTEGRATION_H +#define VULKANSERVERBUFFERINTEGRATION_H + +#include <QtWaylandClient/private/qwayland-wayland.h> +#include "qwayland-qt-vulkan-server-buffer-unstable-v1.h" +#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h> + +#include "vulkanserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtCore/QTextStream> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class VulkanServerBufferIntegration; + +class VulkanServerBuffer : public QWaylandServerBuffer +{ +public: + VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format); + ~VulkanServerBuffer() override; + QOpenGLTexture* toOpenGlTexture() override; + +private: + void import(); + + VulkanServerBufferIntegration *m_integration = nullptr; + struct ::qt_server_buffer *m_server_buffer = nullptr; + QOpenGLTexture *m_texture = nullptr; + int m_fd = -1; + uint m_memorySize = 0; + uint m_internalFormat = 0; + GLuint m_memoryObject = 0; +}; + +class VulkanServerBufferIntegration + : public QWaylandServerBufferIntegration + , public QtWayland::zqt_vulkan_server_buffer_v1 +{ +public: + void initialize(QWaylandDisplay *display) override; + + QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override; + + void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; } + void deleteOrphanedTextures(); + +protected: + void zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) override; + +private: + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, + const QString &interface, uint32_t version); + QWaylandDisplay *m_display = nullptr; + QList<QOpenGLTexture *> orphanedTextures; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/CMakeLists.txt index e75ce07e530..ef3df07a434 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/CMakeLists.txt @@ -13,11 +13,19 @@ qt_internal_add_plugin(QWaylandEglClientBufferPlugin PLUGIN_TYPE wayland-graphics-integration-client SOURCES main.cpp + qwaylandeglclientbufferintegration.cpp qwaylandeglclientbufferintegration_p.h + qwaylandeglinclude_p.h + qwaylandeglwindow.cpp qwaylandeglwindow_p.h + qwaylandglcontext.cpp qwaylandglcontext_p.h LIBRARIES + ${CMAKE_DL_LIBS} + EGL::EGL Qt::Core Qt::Gui + Qt::OpenGLPrivate Qt::WaylandClientPrivate - Qt::WaylandEglClientHwIntegrationPrivate + Wayland::Client + Wayland::Egl QT_LICENSE_ID QT_COMMERCIAL_OR_LGPL3 ) diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/main.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/main.cpp index 9d4fd95edd3..4cb5930bc3b 100644 --- a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/main.cpp +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/main.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtWaylandClient/private/qwaylandclientbufferintegrationplugin_p.h> -#include <QtWaylandEglClientHwIntegration/private/qwaylandeglclientbufferintegration_p.h> +#include "qwaylandeglclientbufferintegration_p.h" QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration.cpp new file mode 100644 index 00000000000..3b97aef208b --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration.cpp @@ -0,0 +1,166 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandeglclientbufferintegration_p.h" + +#include "qwaylandeglwindow_p.h" +#include "qwaylandglcontext_p.h" + +#include <wayland-client-core.h> + +#include <QtCore/QDebug> +#include <private/qeglconvenience_p.h> + +#ifndef EGL_EXT_platform_base +typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list); +#endif + +#ifndef EGL_PLATFORM_WAYLAND_KHR +#define EGL_PLATFORM_WAYLAND_KHR 0x31D8 +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +static const char *qwaylandegl_threadedgl_blacklist_vendor[] = { + 0 +}; + +QWaylandEglClientBufferIntegration::QWaylandEglClientBufferIntegration() +{ + qCDebug(lcQpaWayland) << "Using Wayland-EGL"; +} + + +QWaylandEglClientBufferIntegration::~QWaylandEglClientBufferIntegration() +{ + eglTerminate(m_eglDisplay); +} + +void QWaylandEglClientBufferIntegration::initialize(QWaylandDisplay *display) +{ +#if QT_CONFIG(egl_extension_platform_wayland) + m_eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, display->wl_display(), nullptr); +#else + if (q_hasEglExtension(EGL_NO_DISPLAY, "EGL_EXT_platform_base")) { + if (q_hasEglExtension(EGL_NO_DISPLAY, "EGL_KHR_platform_wayland") || + q_hasEglExtension(EGL_NO_DISPLAY, "EGL_EXT_platform_wayland") || + q_hasEglExtension(EGL_NO_DISPLAY, "EGL_MESA_platform_wayland")) { + + static PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplay = nullptr; + if (!eglGetPlatformDisplay) + eglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + m_eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->wl_display(), nullptr); + } else { + qCWarning(lcQpaWayland) << "The EGL implementation does not support the Wayland platform"; + return; + } + } else { + QByteArray eglPlatform = qgetenv("EGL_PLATFORM"); + if (eglPlatform.isEmpty()) { + setenv("EGL_PLATFORM","wayland",true); + } + + m_eglDisplay = eglGetDisplay((EGLNativeDisplayType) display->wl_display()); + } +#endif + + m_display = display; + + if (m_eglDisplay == EGL_NO_DISPLAY) { + qCWarning(lcQpaWayland) << "EGL not available"; + return; + } + + EGLint major,minor; + if (!eglInitialize(m_eglDisplay, &major, &minor)) { + qCWarning(lcQpaWayland) << "Failed to initialize EGL display" << Qt::hex << eglGetError(); + m_eglDisplay = EGL_NO_DISPLAY; + return; + } + + m_supportsThreading = true; + if (qEnvironmentVariableIsSet("QT_OPENGL_NO_SANITY_CHECK")) + return; + + const char *vendor = eglQueryString(m_eglDisplay, EGL_VENDOR); + for (int i = 0; qwaylandegl_threadedgl_blacklist_vendor[i]; ++i) { + if (strstr(vendor, qwaylandegl_threadedgl_blacklist_vendor[i]) != 0) { + m_supportsThreading = false; + break; + } + } + + // On desktop NVIDIA resizing QtQuick freezes them when using threaded rendering QTBUG-95817 + // In order to support threaded rendering on embedded platforms where resizing is not needed + // we check if XDG_CURRENT_DESKTOP is set which desktop environments should set + if (qstrcmp(vendor, "NVIDIA") == 0 && qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP")) { + m_supportsThreading = false; + } +} + +bool QWaylandEglClientBufferIntegration::isValid() const +{ + return m_eglDisplay != EGL_NO_DISPLAY; +} + +bool QWaylandEglClientBufferIntegration::supportsThreadedOpenGL() const +{ + return m_supportsThreading; +} + +bool QWaylandEglClientBufferIntegration::supportsWindowDecoration() const +{ + return true; +} + +QWaylandWindow *QWaylandEglClientBufferIntegration::createEglWindow(QWindow *window) +{ + return new QWaylandEglWindow(window, m_display); +} + +QPlatformOpenGLContext *QWaylandEglClientBufferIntegration::createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const +{ + QSurfaceFormat fmt = glFormat; + if (m_display->supportsWindowDecoration()) + fmt.setAlphaBufferSize(8); + return new QWaylandGLContext(m_eglDisplay, m_display, fmt, share); +} + +void *QWaylandEglClientBufferIntegration::nativeResource(NativeResource resource) +{ + switch (resource) { + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + +void *QWaylandEglClientBufferIntegration::nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) +{ + Q_ASSERT(context); + switch (resource) { + case EglConfig: + return static_cast<QWaylandGLContext *>(context)->eglConfig(); + case EglContext: + return static_cast<QWaylandGLContext *>(context)->eglContext(); + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + +EGLDisplay QWaylandEglClientBufferIntegration::eglDisplay() const +{ + return m_eglDisplay; +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration_p.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration_p.h new file mode 100644 index 00000000000..ed8fdec2d94 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglclientbufferintegration_p.h @@ -0,0 +1,60 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QWAYLANDEGLINTEGRATION_H +#define QWAYLANDEGLINTEGRATION_H + +#include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> + +#include "qwaylandeglinclude_p.h" + +QT_BEGIN_NAMESPACE + +class QWindow; + +namespace QtWaylandClient { + +class QWaylandWindow; + +class Q_WAYLANDCLIENT_EXPORT QWaylandEglClientBufferIntegration : public QWaylandClientBufferIntegration +{ +public: + QWaylandEglClientBufferIntegration(); + ~QWaylandEglClientBufferIntegration() override; + + void initialize(QWaylandDisplay *display) override; + bool isValid() const override; + bool supportsThreadedOpenGL() const override; + bool supportsWindowDecoration() const override; + + QWaylandWindow *createEglWindow(QWindow *window) override; + QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const override; + + void *nativeResource(NativeResource resource) override; + void *nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) override; + + EGLDisplay eglDisplay() const; + +private: + QWaylandDisplay *m_display = nullptr; + + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + bool m_supportsThreading = false; +}; + +QT_END_NAMESPACE + +} + +#endif // QWAYLANDEGLINTEGRATION_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglinclude_p.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglinclude_p.h new file mode 100644 index 00000000000..eb52ce8eecd --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglinclude_p.h @@ -0,0 +1,27 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QWAYLANDEGLINCLUDE_H +#define QWAYLANDEGLINCLUDE_H + +#include <string.h> +#include <wayland-client-core.h> + +#include <wayland-egl.h> + +#define EGL_EGLEXT_PROTOTYPES +#include <QtGui/private/qt_egl_p.h> + +#endif // QWAYLANDEGLINCLUDE_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow.cpp new file mode 100644 index 00000000000..8375d0e196d --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow.cpp @@ -0,0 +1,201 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandeglwindow_p.h" + +#include <QtWaylandClient/private/qwaylandscreen_p.h> +#include <QtWaylandClient/private/qwaylandsurface_p.h> +#include "qwaylandglcontext_p.h" + +#include <QtGui/private/qeglconvenience_p.h> + +#include <QDebug> +#include <QtGui/QWindow> +#include <qpa/qwindowsysteminterface.h> +#include <QOpenGLFramebufferObject> +#include <QOpenGLContext> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandEglWindow::QWaylandEglWindow(QWindow *window, QWaylandDisplay *display) + : QWaylandWindow(window, display) + , m_clientBufferIntegration(static_cast<QWaylandEglClientBufferIntegration *>(mDisplay->clientBufferIntegration())) +{ + connect(display, &QWaylandDisplay::connected, this, [this] { + m_clientBufferIntegration = static_cast<QWaylandEglClientBufferIntegration *>( + mDisplay->clientBufferIntegration()); + }); + ensureSize(); +} + +QWaylandEglWindow::~QWaylandEglWindow() +{ + if (m_eglSurface) { + eglDestroySurface(m_clientBufferIntegration->eglDisplay(), m_eglSurface); + m_eglSurface = 0; + } + + if (m_waylandEglWindow) + wl_egl_window_destroy(m_waylandEglWindow); + + delete m_contentFBO; +} + +QWaylandWindow::WindowType QWaylandEglWindow::windowType() const +{ + return QWaylandWindow::Egl; +} + +void QWaylandEglWindow::ensureSize() +{ + // this is always called on the main thread + QRect rect = geometry(); + QMargins margins = clientSideMargins(); + QSize sizeWithMargins = (rect.size() + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale(); + { + QWriteLocker lock(&m_bufferSizeLock); + m_bufferSize = sizeWithMargins; + } + + QMutexLocker lock (&m_eglSurfaceLock); + updateSurface(false); +} + +void QWaylandEglWindow::updateSurface(bool create) +{ + // eglSurfaceLock should be locked before calling this method + + QSize sizeWithMargins; + { + QReadLocker lock(&m_bufferSizeLock); + sizeWithMargins = m_bufferSize; + } + + // wl_egl_windows must have both width and height > 0 + // mesa's egl returns NULL if we try to create a, invalid wl_egl_window, however not all EGL + // implementations may do that, so check the size ourself. Besides, we must deal with resizing + // a valid window to 0x0, which would make it invalid. Hence, destroy it. + if (sizeWithMargins.isEmpty()) { + if (m_eglSurface) { + eglDestroySurface(m_clientBufferIntegration->eglDisplay(), m_eglSurface); + m_eglSurface = 0; + } + if (m_waylandEglWindow) { + wl_egl_window_destroy(m_waylandEglWindow); + m_waylandEglWindow = 0; + } + mOffset = QPoint(); + } else { + QReadLocker locker(&mSurfaceLock); + if (m_waylandEglWindow) { + int current_width = 0; + int current_height = 0; + static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt(); + + if (!disableResizeCheck) { + wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height); + } + if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) { + wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y()); + m_requestedSize = sizeWithMargins; + mOffset = QPoint(); + + m_resize = true; + } + } else if (create && mSurface) { + wl_egl_window *eglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height()); + if (Q_UNLIKELY(!eglWindow)) { + qCWarning(lcQpaWayland, "Could not create wl_egl_window with size %dx%d\n", sizeWithMargins.width(), sizeWithMargins.height()); + return; + } + + QSurfaceFormat fmt = window()->requestedFormat(); + if (mDisplay->supportsWindowDecoration()) + fmt.setAlphaBufferSize(8); + EGLConfig eglConfig = q_configFromGLFormat(m_clientBufferIntegration->eglDisplay(), fmt); + setFormat(q_glFormatFromConfig(m_clientBufferIntegration->eglDisplay(), eglConfig, fmt)); + + EGLSurface eglSurface = eglCreateWindowSurface(m_clientBufferIntegration->eglDisplay(), eglConfig, (EGLNativeWindowType) eglWindow, 0); + if (Q_UNLIKELY(eglSurface == EGL_NO_SURFACE)) { + qCWarning(lcQpaWayland, "Could not create EGL surface (EGL error 0x%x)\n", eglGetError()); + wl_egl_window_destroy(eglWindow); + return; + } + + m_waylandEglWindow = eglWindow; + m_eglSurface = eglSurface; + m_requestedSize = sizeWithMargins; + } + } +} + +QRect QWaylandEglWindow::contentsRect() const +{ + QRect r = geometry(); + QMargins m = clientSideMargins(); + return QRect(m.left(), m.bottom(), r.width(), r.height()); +} + +void QWaylandEglWindow::invalidateSurface() +{ + QMutexLocker lock (&m_eglSurfaceLock); + + if (m_eglSurface) { + eglDestroySurface(m_clientBufferIntegration->eglDisplay(), m_eglSurface); + m_eglSurface = 0; + } + if (m_waylandEglWindow) { + wl_egl_window_destroy(m_waylandEglWindow); + m_waylandEglWindow = nullptr; + } + delete m_contentFBO; + m_contentFBO = nullptr; +} + +EGLSurface QWaylandEglWindow::eglSurface() const +{ + return m_eglSurface; +} + +QMutex* QWaylandEglWindow::eglSurfaceLock() +{ + return &m_eglSurfaceLock; +} + +GLuint QWaylandEglWindow::contentFBO() const +{ + if (!decoration()) + return 0; + + if (m_resize || !m_contentFBO) { + QOpenGLFramebufferObject *old = m_contentFBO; + QSize fboSize = geometry().size() * scale(); + m_contentFBO = new QOpenGLFramebufferObject(fboSize.width(), fboSize.height(), QOpenGLFramebufferObject::CombinedDepthStencil); + + delete old; + m_resize = false; + } + + return m_contentFBO->handle(); +} + +GLuint QWaylandEglWindow::contentTexture() const +{ + return m_contentFBO->texture(); +} + +void QWaylandEglWindow::bindContentFBO() +{ + if (decoration()) { + contentFBO(); + m_contentFBO->bind(); + } +} + +} + +QT_END_NAMESPACE + +#include "moc_qwaylandeglwindow_p.cpp" diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow_p.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow_p.h new file mode 100644 index 00000000000..dc2e0319519 --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandeglwindow_p.h @@ -0,0 +1,76 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QWAYLANDEGLWINDOW_H +#define QWAYLANDEGLWINDOW_H + +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include "qwaylandeglinclude_p.h" +#include "qwaylandeglclientbufferintegration_p.h" + +QT_BEGIN_NAMESPACE + +class QOpenGLFramebufferObject; + +namespace QtWaylandClient { + +class QWaylandGLContext; + +class Q_WAYLANDCLIENT_EXPORT QWaylandEglWindow : public QWaylandWindow +{ + Q_OBJECT +public: + QWaylandEglWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandEglWindow(); + WindowType windowType() const override; + void ensureSize() override; + + void updateSurface(bool create); + QRect contentsRect() const; + + EGLSurface eglSurface() const; + GLuint contentFBO() const; + GLuint contentTexture() const; + bool needToUpdateContentFBO() const { return decoration() && (m_resize || !m_contentFBO); } + + void bindContentFBO(); + + void invalidateSurface() override; + + QMutex* eglSurfaceLock(); + +private: + QWaylandEglClientBufferIntegration *m_clientBufferIntegration = nullptr; + struct wl_egl_window *m_waylandEglWindow = nullptr; + + // Locks any manipulation of the eglSurface size + QMutex m_eglSurfaceLock; + EGLSurface m_eglSurface = EGL_NO_SURFACE; + mutable bool m_resize = false; + mutable QOpenGLFramebufferObject *m_contentFBO = nullptr; + + // Size used in the last call to wl_egl_window_resize + QSize m_requestedSize; + + // Size of the buffer used by QWaylandWindow + // This is always written to from the main thread, potentially read from the rendering thread + QReadWriteLock m_bufferSizeLock; + QSize m_bufferSize; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDEGLWINDOW_H diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext.cpp b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext.cpp new file mode 100644 index 00000000000..be5a36fa01a --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext.cpp @@ -0,0 +1,430 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandglcontext_p.h" + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtWaylandClient/private/qwaylandsubsurface_p.h> +#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h> +#include <QtWaylandClient/private/qwaylandintegration_p.h> +#include "qwaylandeglwindow_p.h" + +#include <QDebug> +#include <QtGui/private/qeglconvenience_p.h> +#include <QtGui/private/qopenglcontext_p.h> +#include <QtOpenGL/private/qopengltexturecache_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformopenglcontext.h> +#include <QtGui/QSurfaceFormat> +#include <QtOpenGL/QOpenGLShaderProgram> +#include <QtGui/QOpenGLFunctions> +#include <QOpenGLBuffer> + +#include <QtCore/qmutex.h> + +#include <dlfcn.h> + +// Constants from EGL_KHR_create_context +#ifndef EGL_CONTEXT_MINOR_VERSION_KHR +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB +#endif +#ifndef EGL_CONTEXT_FLAGS_KHR +#define EGL_CONTEXT_FLAGS_KHR 0x30FC +#endif +#ifndef EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD +#endif +#ifndef EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#endif +#ifndef EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#endif +#ifndef EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#endif +#ifndef EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#endif + +// Constants for OpenGL which are not available in the ES headers. +#ifndef GL_CONTEXT_FLAGS +#define GL_CONTEXT_FLAGS 0x821E +#endif +#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif +#ifndef GL_CONTEXT_FLAG_DEBUG_BIT +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#endif +#ifndef GL_CONTEXT_PROFILE_MASK +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#endif +#ifndef GL_CONTEXT_CORE_PROFILE_BIT +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#endif +#ifndef GL_CONTEXT_COMPATIBILITY_PROFILE_BIT +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#endif + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class DecorationsBlitter : public QOpenGLFunctions +{ +public: + DecorationsBlitter(QWaylandGLContext *context) + : m_context(context) + { + initializeOpenGLFunctions(); + m_blitProgram = new QOpenGLShaderProgram(); + m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\ + attribute vec4 texCoords;\n\ + varying vec2 outTexCoords;\n\ + void main()\n\ + {\n\ + gl_Position = position;\n\ + outTexCoords = texCoords.xy;\n\ + }"); + m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\ + uniform sampler2D texture;\n\ + void main()\n\ + {\n\ + gl_FragColor = texture2D(texture, outTexCoords);\n\ + }"); + + m_blitProgram->bindAttributeLocation("position", 0); + m_blitProgram->bindAttributeLocation("texCoords", 1); + + if (!m_blitProgram->link()) { + qDebug() << "Shader Program link failed."; + qDebug() << m_blitProgram->log(); + } + + m_blitProgram->bind(); + m_blitProgram->enableAttributeArray(0); + m_blitProgram->enableAttributeArray(1); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_FALSE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + m_buffer.create(); + m_buffer.bind(); + + static const GLfloat squareVertices[] = { + -1.f, -1.f, + 1.0f, -1.f, + -1.f, 1.0f, + 1.0f, 1.0f + }; + static const GLfloat inverseSquareVertices[] = { + -1.f, 1.f, + 1.f, 1.f, + -1.f, -1.f, + 1.f, -1.f + }; + static const GLfloat textureVertices[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + }; + + m_squareVerticesOffset = 0; + m_inverseSquareVerticesOffset = sizeof(squareVertices); + m_textureVerticesOffset = sizeof(squareVertices) + sizeof(textureVertices); + + m_buffer.allocate(sizeof(squareVertices) + sizeof(inverseSquareVertices) + sizeof(textureVertices)); + m_buffer.write(m_squareVerticesOffset, squareVertices, sizeof(squareVertices)); + m_buffer.write(m_inverseSquareVerticesOffset, inverseSquareVertices, sizeof(inverseSquareVertices)); + m_buffer.write(m_textureVerticesOffset, textureVertices, sizeof(textureVertices)); + + m_blitProgram->setAttributeBuffer(1, GL_FLOAT, m_textureVerticesOffset, 2); + + m_textureWrap = m_context->context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + } + ~DecorationsBlitter() + { + delete m_blitProgram; + } + void blit(QWaylandEglWindow *window) + { + QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context()); + + QSize surfaceSize = window->surfaceSize(); + qreal scale = window->scale() ; + glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale); + + //Draw Decoration + if (auto *decoration = window->decoration()) { + m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_inverseSquareVerticesOffset, 2); + QImage decorationImage = decoration->contentImage(); + cache->bindTexture(m_context->context(), decorationImage); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_textureWrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_textureWrap); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + //Draw Content + m_blitProgram->setAttributeBuffer(0, GL_FLOAT, m_squareVerticesOffset, 2); + glBindTexture(GL_TEXTURE_2D, window->contentTexture()); + QRect r = window->contentsRect(); + glViewport(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + QOpenGLShaderProgram *m_blitProgram = nullptr; + QWaylandGLContext *m_context = nullptr; + QOpenGLBuffer m_buffer; + int m_squareVerticesOffset; + int m_inverseSquareVerticesOffset; + int m_textureVerticesOffset; + int m_textureWrap; +}; + +QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *display, + const QSurfaceFormat &fmt, QPlatformOpenGLContext *share) + : QEGLPlatformContext(fmt, share, eglDisplay) + , m_display(display) + , m_decorationsContext(EGL_NO_CONTEXT) +{ + m_reconnectionWatcher = QObject::connect(m_display, &QWaylandDisplay::connected, + m_display, [this] { invalidateContext(); }); + + switch (format().renderableType()) { + case QSurfaceFormat::OpenVG: + m_api = EGL_OPENVG_API; + break; +#ifdef EGL_VERSION_1_4 + case QSurfaceFormat::OpenGL: + m_api = EGL_OPENGL_API; + break; +#endif // EGL_VERSION_1_4 + default: + m_api = EGL_OPENGL_ES_API; + break; + } + + if (m_display->supportsWindowDecoration()) { + // Create an EGL context for the decorations blitter. By using a dedicated context we are free to + // change its state and we also use OpenGL ES 2 API independently to what the app is using to draw. + QList<EGLint> eglDecorationsContextAttrs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + m_decorationsContext = eglCreateContext(eglDisplay, eglConfig(), eglContext(), + eglDecorationsContextAttrs.constData()); + if (m_decorationsContext == EGL_NO_CONTEXT) + qWarning("QWaylandGLContext: Failed to create the decorations EGLContext. Decorations will not be drawn."); + } + + EGLint a = EGL_MIN_SWAP_INTERVAL; + EGLint b = EGL_MAX_SWAP_INTERVAL; + if (!eglGetConfigAttrib(eglDisplay, eglConfig(), a, &a) + || !eglGetConfigAttrib(eglDisplay, eglConfig(), b, &b) || a > 0) { + m_supportNonBlockingSwap = false; + } + { + bool ok; + int supportNonBlockingSwap = qEnvironmentVariableIntValue("QT_WAYLAND_FORCE_NONBLOCKING_SWAP_SUPPORT", &ok); + if (ok) + m_supportNonBlockingSwap = supportNonBlockingSwap != 0; + } + if (!m_supportNonBlockingSwap) { + qCWarning(lcQpaWayland) << "Non-blocking swap buffers not supported." + << "Subsurface rendering can be affected." + << "It may also cause the event loop to freeze in some situations"; + } +} + +EGLSurface QWaylandGLContext::createTemporaryOffscreenSurface() +{ + m_wlSurface = m_display->createSurface(nullptr); + m_eglWindow = wl_egl_window_create(m_wlSurface, 1, 1); +#if QT_CONFIG(egl_extension_platform_wayland) + EGLSurface eglSurface = + eglCreatePlatformWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr); +#else + EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay(), eglConfig(), m_eglWindow, nullptr); +#endif + return eglSurface; +} + +void QWaylandGLContext::destroyTemporaryOffscreenSurface(EGLSurface eglSurface) +{ + eglDestroySurface(eglDisplay(), eglSurface); + wl_egl_window_destroy(m_eglWindow); + m_eglWindow = nullptr; + wl_surface_destroy(m_wlSurface); + m_wlSurface = nullptr; +} + +void QWaylandGLContext::runGLChecks() +{ + bool ok; + const int doneCurrentWorkAround = qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND", &ok); + if (ok) { + m_doneCurrentWorkAround = doneCurrentWorkAround != 0; + if (m_doneCurrentWorkAround) + qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround on request."; + else + qCDebug(lcQpaWayland) << "Disabling doneCurrent() workaround on request."; + + } else { + // Note that even though there is an EGL context current here, + // QOpenGLContext and QOpenGLFunctions are not yet usable at this stage. + const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER)); + if (renderer && strstr(renderer, "Mali")) { + qCDebug(lcQpaWayland) << "Enabling doneCurrent() workaround for Mali GPU." + << "Set QT_WAYLAND_ENABLE_DONECURRENT_WORKAROUND=0 to disable."; + m_doneCurrentWorkAround = true; + } + } + + QEGLPlatformContext::runGLChecks(); +} + +QWaylandGLContext::~QWaylandGLContext() +{ + QObject::disconnect(m_reconnectionWatcher); + delete m_blitter; + m_blitter = nullptr; + if (m_decorationsContext != EGL_NO_CONTEXT) + eglDestroyContext(eglDisplay(), m_decorationsContext); +} + +void QWaylandGLContext::beginFrame() +{ + Q_ASSERT(m_currentWindow != nullptr); + if (m_supportNonBlockingSwap) + m_currentWindow->beginFrame(); +} + +void QWaylandGLContext::endFrame() +{ + Q_ASSERT(m_currentWindow != nullptr); + if (m_doneCurrentWorkAround) { + doneCurrent(); + QOpenGLContextPrivate::setCurrentContext(nullptr); + } + + if (m_supportNonBlockingSwap) + m_currentWindow->endFrame(); +} + +bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) +{ + if (!isValid()) { + return false; + } + + // in QWaylandGLContext() we called eglBindAPI with the correct value. However, + // eglBindAPI's documentation says: + // "eglBindAPI defines the current rendering API for EGL in the thread it is called from" + // Since makeCurrent() can be called from a different thread than the one we created the + // context in make sure to call eglBindAPI in the correct thread. + if (eglQueryAPI() != m_api) { + eglBindAPI(m_api); + } + + m_currentWindow = static_cast<QWaylandEglWindow *>(surface); + + QMutexLocker lock(m_currentWindow->eglSurfaceLock()); + EGLSurface eglSurface = m_currentWindow->eglSurface(); + + if (!m_currentWindow->needToUpdateContentFBO() && (eglSurface != EGL_NO_SURFACE)) { + if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) { + qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this); + return false; + } + return true; + } + + if (eglSurface == EGL_NO_SURFACE) { + m_currentWindow->updateSurface(true); + eglSurface = m_currentWindow->eglSurface(); + } + + if (!eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext())) { + qWarning("QWaylandGLContext::makeCurrent: eglError: %#x, this: %p \n", eglGetError(), this); + return false; + } + + //### setCurrentContext will be called in QOpenGLContext::makeCurrent after this function + // returns, but that's too late, as we need a current context in order to bind the content FBO. + QOpenGLContextPrivate::setCurrentContext(context()); + m_currentWindow->bindContentFBO(); + + return true; +} + +void QWaylandGLContext::doneCurrent() +{ + eglMakeCurrent(eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) +{ + QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface); + + EGLSurface eglSurface = window->eglSurface(); + + if (window->decoration()) { + if (m_api != EGL_OPENGL_ES_API) + eglBindAPI(EGL_OPENGL_ES_API); + + // save the current EGL content and surface to set it again after the blitter is done + EGLDisplay currentDisplay = eglGetCurrentDisplay(); + EGLContext currentContext = eglGetCurrentContext(); + EGLSurface currentSurfaceDraw = eglGetCurrentSurface(EGL_DRAW); + EGLSurface currentSurfaceRead = eglGetCurrentSurface(EGL_READ); + eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, m_decorationsContext); + + if (!m_blitter) + m_blitter = new DecorationsBlitter(this); + m_blitter->blit(window); + + if (m_api != EGL_OPENGL_ES_API) + eglBindAPI(m_api); + eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext); + } + + int swapInterval = m_supportNonBlockingSwap ? 0 : format().swapInterval(); + eglSwapInterval(eglDisplay(), swapInterval); + if (swapInterval == 0 && format().swapInterval() > 0) { + // Emulating a blocking swap + glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives + window->waitForFrameSync(100); + } + window->handleUpdate(); + if (!eglSwapBuffers(eglDisplay(), eglSurface)) + qCWarning(lcQpaWayland, "eglSwapBuffers failed with %#x, surface: %p", eglGetError(), eglSurface); +} + +GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const +{ + return static_cast<QWaylandEglWindow *>(surface)->contentFBO(); +} + +QFunctionPointer QWaylandGLContext::getProcAddress(const char *procName) +{ + QFunctionPointer proc = (QFunctionPointer) eglGetProcAddress(procName); + if (!proc) + proc = (QFunctionPointer) dlsym(RTLD_DEFAULT, procName); + return proc; +} + +EGLSurface QWaylandGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) +{ + return static_cast<QWaylandEglWindow *>(surface)->eglSurface(); +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext_p.h b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext_p.h new file mode 100644 index 00000000000..bd9eb53f85e --- /dev/null +++ b/src/plugins/platforms/wayland/plugins/hardwareintegration/wayland-egl/qwaylandglcontext_p.h @@ -0,0 +1,74 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QWAYLANDGLCONTEXT_H +#define QWAYLANDGLCONTEXT_H + +#include "qwaylandeglinclude_p.h" //must be first + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtGui/private/qeglplatformcontext_p.h> +#include <qpa/qplatformopenglcontext.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; +class QOpenGLTextureCache; + +namespace QtWaylandClient { + +class QWaylandEglWindow; +class DecorationsBlitter; + +class Q_WAYLANDCLIENT_EXPORT QWaylandGLContext : public QEGLPlatformContext +{ +public: + QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *display, const QSurfaceFormat &format, QPlatformOpenGLContext *share); + ~QWaylandGLContext(); + void swapBuffers(QPlatformSurface *surface) override; + + bool makeCurrent(QPlatformSurface *surface) override; + void doneCurrent() override; + + void beginFrame() override; + void endFrame() override; + + GLuint defaultFramebufferObject(QPlatformSurface *surface) const override; + + QFunctionPointer getProcAddress(const char *procName) override; + +protected: + EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override; + EGLSurface createTemporaryOffscreenSurface() override; + void destroyTemporaryOffscreenSurface(EGLSurface surface) override; + void runGLChecks() override; + +private: + QWaylandDisplay *m_display = nullptr; + EGLContext m_decorationsContext; + DecorationsBlitter *m_blitter = nullptr; + bool m_supportNonBlockingSwap = true; + EGLenum m_api; + wl_surface *m_wlSurface = nullptr; + wl_egl_window *m_eglWindow = nullptr; + QWaylandEglWindow *m_currentWindow = nullptr; + QMetaObject::Connection m_reconnectionWatcher; + bool m_doneCurrentWorkAround = false; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDGLCONTEXT_H |