diff options
author | Paul Olav Tvete <paul.tvete@qt.io> | 2019-01-21 10:06:49 +0100 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@qt.io> | 2019-08-13 11:56:10 +0200 |
commit | 43d8a3091894ceb4ab934167b2f3eda27564eb6d (patch) | |
tree | 67739f82b8ed86ad92fa7f3ee0005ce86edf8aaf /src/hardwareintegration | |
parent | 3d5cec736ce17c6b40c52bb8966f8fc40b742664 (diff) |
Backport texture sharing for NVIDIA
This commit backports the Vulkan server buffer and texture sharing code
from Qt 5.14 to Qt 5.12 as an opt-in feature. To enable, configure with
"-feature-wayland-client-texture-sharing-experimental
-feature-wayland-compositor-texture-sharing-experimental"
Contains code from the following commits:
Add server buffer integration based on Vulkan
(commit df3a1761af2f20d59ae09a7adaa2f5b959047687)
Compressed texture support for vulkan server buffers
(commit f710489a341713c675cfd91d22ccd7bf8f29f4dd)
Implement server-side toOpenGlTexture for Vulkan
(commit 19361e7259f04b08925b1e8e99faf9460770ee7b)
New texture sharing protocol and infrastructure
(commit 80001cbf0451f4ba2a971fb20b80dc8e25ac604d)
Change-Id: I6c36ef7fddcd4db39e80d03a822d89f15eae3434
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Johan Helsing <johan.helsing@qt.io>
Diffstat (limited to 'src/hardwareintegration')
8 files changed, 1585 insertions, 0 deletions
diff --git a/src/hardwareintegration/client/vulkan-server/vulkan-server.pri b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri new file mode 100644 index 000000000..2e13a87b7 --- /dev/null +++ b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD + +QMAKE_USE += wayland-client + +SOURCES += \ + $$PWD/vulkanserverbufferintegration.cpp + +HEADERS += \ + $$PWD/vulkanserverbufferintegration.h + +CONFIG += wayland-scanner +WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml diff --git a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp new file mode 100644 index 000000000..7e1d5966f --- /dev/null +++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define GL_GLEXT_PROTOTYPES +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include "vulkanserverbufferintegration.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QDebug> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <QtGui/QImage> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +static constexpr bool extraDebug = +#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 (extraDebug) 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 (extraDebug) qDebug() << "importing" << m_fd << hex << glGetError(); + + auto *glContext = QOpenGLContext::currentContext(); + if (!glContext) + return; + + if (!funcs && !VulkanServerBufferGlFunctions::create(glContext)) + return; + + funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject); + if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError(); + funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, m_fd); + if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError(); + + + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + + if (extraDebug) qDebug() << "created texture" << m_texture->textureId() << hex << glGetError(); + + m_texture->bind(); + if (extraDebug) qDebug() << "bound texture" << hex << glGetError(); + funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_internalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 ); + if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError(); + if (extraDebug) qDebug() << "format" << 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 (extraDebug) 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/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h new file mode 100644 index 000000000..7add74269 --- /dev/null +++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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; + QVector<QOpenGLTexture *> orphanedTextures; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri new file mode 100644 index 000000000..63a96ad0f --- /dev/null +++ b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri @@ -0,0 +1,16 @@ +INCLUDEPATH += $$PWD $$PWD/../../../3rdparty/util + +QT += vulkan_support-private + +QMAKE_USE_PRIVATE += wayland-server + +SOURCES += \ + $$PWD/vulkanserverbufferintegration.cpp \ + $$PWD/vulkanwrapper.cpp + +HEADERS += \ + $$PWD/vulkanserverbufferintegration.h \ + $$PWD/vulkanwrapper.h + +CONFIG += wayland-scanner +WAYLANDSERVERSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp new file mode 100644 index 000000000..7f9f8a151 --- /dev/null +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define GL_GLEXT_PROTOTYPES +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include "vulkanserverbufferintegration.h" + +#include "vulkanwrapper.h" + +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <QtGui/QOffscreenSurface> + +#include <unistd.h> +#include <fcntl.h> + +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE +static constexpr bool extraDebug = false; + +#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; + +//RAII +class CurrentContext +{ +public: + CurrentContext() + { + if (!QOpenGLContext::currentContext()) { + if (QOpenGLContext::globalShareContext()) { + if (!localContext) { + localContext = new QOpenGLContext; + localContext->setShareContext(QOpenGLContext::globalShareContext()); + localContext->create(); + } + if (!offscreenSurface) { + offscreenSurface = new QOffscreenSurface; + offscreenSurface->setFormat(localContext->format()); + offscreenSurface->create(); + } + localContext->makeCurrent(offscreenSurface); + localContextInUse = true; + } else { + qCritical("VulkanServerBufferIntegration: no globalShareContext"); + } + } + } + ~CurrentContext() + { + if (localContextInUse) + localContext->doneCurrent(); + } + QOpenGLContext *context() { return localContextInUse ? localContext : QOpenGLContext::currentContext(); } +private: + static QOpenGLContext *localContext; + static QOffscreenSurface *offscreenSurface; + bool localContextInUse = false; +}; + +QOpenGLContext *CurrentContext::localContext = nullptr; +QOffscreenSurface *CurrentContext::offscreenSurface = 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, const QImage &qimage, QtWayland::ServerBuffer::Format format) + : QtWayland::ServerBuffer(qimage.size(),format) + , m_integration(integration) + , m_width(qimage.width()) + , m_height(qimage.height()) +{ + m_format = format; + switch (m_format) { + case RGBA32: + m_glInternalFormat = GL_RGBA8; + break; + // case A8: + // m_glInternalFormat = GL_R8; + // break; + default: + qWarning("VulkanServerBuffer: unsupported format"); + m_glInternalFormat = GL_RGBA8; + break; + } + + auto vulkanWrapper = m_integration->vulkanWrapper(); + m_vImage = vulkanWrapper->createTextureImage(qimage); + if (m_vImage) + m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize); +} + +VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size) + : QtWayland::ServerBuffer(size, QtWayland::ServerBuffer::Custom) + , m_integration(integration) + , m_width(size.width()) + , m_height(size.height()) + , m_vImage(vImage) + , m_glInternalFormat(glInternalFormat) +{ + auto vulkanWrapper = m_integration->vulkanWrapper(); + m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize); +} + +VulkanServerBuffer::~VulkanServerBuffer() +{ + delete m_texture; //this is always nullptr for now + auto vulkanWrapper = m_integration->vulkanWrapper(); + vulkanWrapper->freeTextureImage(m_vImage); +} + +struct ::wl_resource *VulkanServerBuffer::resourceForClient(struct ::wl_client *client) +{ + auto *bufferResource = resourceMap().value(client); + if (!bufferResource) { + auto integrationResource = m_integration->resourceMap().value(client); + if (!integrationResource) { + qWarning("VulkanServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the vulkan interface"); + return nullptr; + } + struct ::wl_resource *shm_integration_resource = integrationResource->handle; + Resource *resource = add(client, 1); + m_integration->send_server_buffer_created(shm_integration_resource, resource->handle, m_fd, m_width, m_height, m_memorySize, m_glInternalFormat); + return resource->handle; + } + return bufferResource->handle; +} + +QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture() +{ + if (m_texture && m_texture->isCreated()) + return m_texture; + + CurrentContext current; + + if (!funcs && !VulkanServerBufferGlFunctions::create(current.context())) + return nullptr; + + funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject); + if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError(); + + + int dupfd = fcntl(m_fd, F_DUPFD_CLOEXEC, 0); + if (dupfd < 0) { + perror("VulkanServerBuffer::toOpenGlTexture() Could not dup fd:"); + return nullptr; + } + + funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, dupfd); + if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError(); + + + if (!m_texture) + m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + m_texture->create(); + + GLuint texId = m_texture->textureId(); + if (extraDebug) qDebug() << "created texture" << texId << hex << glGetError(); + + m_texture->bind(); + if (extraDebug) qDebug() << "bound texture" << texId << hex << glGetError(); + funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_glInternalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 ); + if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError(); + if (extraDebug) qDebug() << "format" << hex << m_glInternalFormat << GL_RGBA8; + + + return m_texture; +} + +void VulkanServerBuffer::releaseOpenGlTexture() +{ + if (!m_texture || !m_texture->isCreated()) + return; + + CurrentContext current; + m_texture->destroy(); + funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject); +} + + +bool VulkanServerBuffer::bufferInUse() +{ + return (m_texture && m_texture->isCreated()) || resourceMap().count() > 0; +} + +void VulkanServerBuffer::server_buffer_release(Resource *resource) +{ + qCDebug(qLcWaylandCompositorHardwareIntegration) << "server_buffer RELEASE resource" << resource->handle << wl_resource_get_id(resource->handle) << "for client" << resource->client(); + wl_resource_destroy(resource->handle); +} + +VulkanServerBufferIntegration::VulkanServerBufferIntegration() +{ +} + +VulkanServerBufferIntegration::~VulkanServerBufferIntegration() +{ +} + +void VulkanServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor) +{ + Q_ASSERT(QGuiApplication::platformNativeInterface()); + + QtWaylandServer::zqt_vulkan_server_buffer_v1::init(compositor->display(), 1); +} + +bool VulkanServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const +{ + switch (format) { + case QtWayland::ServerBuffer::RGBA32: + return true; + case QtWayland::ServerBuffer::A8: + return false; + default: + return false; + } +} + +QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) +{ + if (!m_vulkanWrapper) { + CurrentContext current; + m_vulkanWrapper = new VulkanWrapper(current.context()); + } + return new VulkanServerBuffer(this, qimage, format); +} + +QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) +{ + if (!m_vulkanWrapper) { + CurrentContext current; + m_vulkanWrapper = new VulkanWrapper(current.context()); + } + + auto *vImage = m_vulkanWrapper->createTextureImageFromData(reinterpret_cast<const uchar*>(data.constData()), data.size(), size, glInternalFormat); + + if (vImage) + return new VulkanServerBuffer(this, vImage, glInternalFormat, size); + + qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not load compressed texture"; + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h new file mode 100644 index 000000000..7246e36df --- /dev/null +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VULKANSERVERBUFFERINTEGRATION_H +#define VULKANSERVERBUFFERINTEGRATION_H + +#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h> + +#include "qwayland-server-qt-vulkan-server-buffer-unstable-v1.h" + +#include <QtGui/QImage> +#include <QtGui/QWindow> +#include <QtGui/qpa/qplatformnativeinterface.h> +#include <QtGui/QGuiApplication> + +#include <QtWaylandCompositor/qwaylandcompositor.h> +#include <QtWaylandCompositor/private/qwayland-server-server-buffer-extension.h> + +QT_BEGIN_NAMESPACE + +class VulkanServerBufferIntegration; +class VulkanWrapper; +struct VulkanImageWrapper; + +class VulkanServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_server_buffer +{ +public: + VulkanServerBuffer(VulkanServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format); + VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size); + ~VulkanServerBuffer() override; + + struct ::wl_resource *resourceForClient(struct ::wl_client *) override; + bool bufferInUse() override; + QOpenGLTexture *toOpenGlTexture() override; + void releaseOpenGlTexture() override; + +protected: + void server_buffer_release(Resource *resource) override; + +private: + VulkanServerBufferIntegration *m_integration = nullptr; + + int m_width; + int m_height; + int m_memorySize; + int m_fd = -1; + VulkanImageWrapper *m_vImage = nullptr; + QOpenGLTexture *m_texture = nullptr; + uint m_glInternalFormat; + GLuint m_memoryObject; +}; + +class VulkanServerBufferIntegration : + public QtWayland::ServerBufferIntegration, + public QtWaylandServer::zqt_vulkan_server_buffer_v1 +{ +public: + VulkanServerBufferIntegration(); + ~VulkanServerBufferIntegration() override; + + VulkanWrapper *vulkanWrapper() const { return m_vulkanWrapper; } + + void initializeHardware(QWaylandCompositor *) override; + + bool supportsFormat(QtWayland::ServerBuffer::Format format) const override; + QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override; + QtWayland::ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) override; + +private: + VulkanWrapper *m_vulkanWrapper = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp new file mode 100644 index 000000000..fe66adf55 --- /dev/null +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp @@ -0,0 +1,733 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// NOTE: Some of the code below is adapted from the public domain code at https://vulkan-tutorial.com/ + +#define GL_GLEXT_PROTOTYPES +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <QtVulkanSupport/private/qvkconvenience_p.h> + +#include "vulkanwrapper.h" + +#include <QImage> +#include <QOpenGLContext> + +#include <set> + +#include <unistd.h> + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +static constexpr bool extraDebug = false; + +#define DECL_VK_FUNCTION(name) \ + PFN_ ## name name = nullptr; + +#define IMPL_VK_FUNCTION(name) \ + name = reinterpret_cast<PFN_ ## name>(f_glGetVkProcAddrNV(#name)); \ + if (!name) { \ + qCritical() << "ERROR in Vulkan proc lookup. Could not find " #name; \ + } + +struct QueueFamilyIndices { + int graphicsFamily = -1; + int presentFamily = -1; + + bool isComplete() { + return graphicsFamily >= 0 && presentFamily >= 0; + } +}; + +class VulkanWrapperPrivate +{ +public: + explicit VulkanWrapperPrivate(QOpenGLContext *glContext); + + VulkanImageWrapper *createTextureImage(const QImage &img); + VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat); + + void freeTextureImage(VulkanImageWrapper *imageWrapper); + +private: + DECL_VK_FUNCTION(vkAllocateCommandBuffers); + DECL_VK_FUNCTION(vkAllocateMemory); + DECL_VK_FUNCTION(vkBeginCommandBuffer); + DECL_VK_FUNCTION(vkBindImageMemory); + DECL_VK_FUNCTION(vkCmdCopyBufferToImage); + DECL_VK_FUNCTION(vkCmdPipelineBarrier); + DECL_VK_FUNCTION(vkCreateImage); + DECL_VK_FUNCTION(vkDestroyImage); + DECL_VK_FUNCTION(vkDestroyBuffer); + DECL_VK_FUNCTION(vkEndCommandBuffer); + DECL_VK_FUNCTION(vkFreeCommandBuffers); + DECL_VK_FUNCTION(vkFreeMemory); + DECL_VK_FUNCTION(vkGetImageMemoryRequirements); + DECL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties); + DECL_VK_FUNCTION(vkMapMemory); + DECL_VK_FUNCTION(vkQueueSubmit); + DECL_VK_FUNCTION(vkQueueWaitIdle); + DECL_VK_FUNCTION(vkUnmapMemory); + DECL_VK_FUNCTION(vkCreateBuffer); + DECL_VK_FUNCTION(vkGetBufferMemoryRequirements); + DECL_VK_FUNCTION(vkBindBufferMemory); + + DECL_VK_FUNCTION(vkCreateInstance); + DECL_VK_FUNCTION(vkEnumeratePhysicalDevices); + DECL_VK_FUNCTION(vkGetPhysicalDeviceProperties); + DECL_VK_FUNCTION(vkCreateDevice); + DECL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties); + + DECL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); + DECL_VK_FUNCTION(vkCreateCommandPool); + + DECL_VK_FUNCTION(vkGetDeviceQueue); + DECL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR); + DECL_VK_FUNCTION(vkGetMemoryFdKHR); + + //DECL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); + + void initFunctions(PFNGLGETVKPROCADDRNVPROC f_glGetVkProcAddrNV) { + IMPL_VK_FUNCTION(vkAllocateCommandBuffers); + IMPL_VK_FUNCTION(vkAllocateMemory); + IMPL_VK_FUNCTION(vkBeginCommandBuffer); + IMPL_VK_FUNCTION(vkBindImageMemory); + IMPL_VK_FUNCTION(vkCmdCopyBufferToImage); + IMPL_VK_FUNCTION(vkCmdPipelineBarrier); + IMPL_VK_FUNCTION(vkCreateImage); + IMPL_VK_FUNCTION(vkDestroyImage); + IMPL_VK_FUNCTION(vkDestroyBuffer); + IMPL_VK_FUNCTION(vkEndCommandBuffer); + IMPL_VK_FUNCTION(vkFreeCommandBuffers); + IMPL_VK_FUNCTION(vkFreeMemory); + IMPL_VK_FUNCTION(vkGetImageMemoryRequirements); + IMPL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties); + IMPL_VK_FUNCTION(vkMapMemory); + IMPL_VK_FUNCTION(vkQueueSubmit); + IMPL_VK_FUNCTION(vkQueueWaitIdle); + IMPL_VK_FUNCTION(vkUnmapMemory); + IMPL_VK_FUNCTION(vkCreateBuffer); + IMPL_VK_FUNCTION(vkGetBufferMemoryRequirements); + IMPL_VK_FUNCTION(vkBindBufferMemory); + + IMPL_VK_FUNCTION(vkCreateInstance); + IMPL_VK_FUNCTION(vkEnumeratePhysicalDevices); + IMPL_VK_FUNCTION(vkGetPhysicalDeviceProperties); + IMPL_VK_FUNCTION(vkCreateDevice); + IMPL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties); + + IMPL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); + IMPL_VK_FUNCTION(vkCreateCommandPool); + + IMPL_VK_FUNCTION(vkGetDeviceQueue); + IMPL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR); + IMPL_VK_FUNCTION(vkGetMemoryFdKHR); + + //IMPL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); + } + + int findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + + VulkanImageWrapper *createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize); + bool transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout); + bool createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); + VkCommandBuffer beginSingleTimeCommands(); + void endSingleTimeCommands(VkCommandBuffer commandBuffer); + void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height); + void createCommandPool(); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + bool createLogicalDevice(); + +private: + VkInstance m_instance = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + VkDevice m_device = VK_NULL_HANDLE; + VkCommandPool m_commandPool = VK_NULL_HANDLE; + + VkQueue m_graphicsQueue = VK_NULL_HANDLE; + + bool m_initFailed = false; +}; + +struct VulkanImageWrapper +{ + VkImage textureImage = VK_NULL_HANDLE; + int imgMemSize = -1; + QSize imgSize; + int imgFd = -1; + VkDeviceMemory textureImageMemory = VK_NULL_HANDLE; +}; + +int VulkanWrapperPrivate::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) +{ + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); + + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + + qCritical("VulkanWrapper: failed to find suitable memory type!"); + return -1; +} + + +VulkanImageWrapper *VulkanWrapperPrivate::createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize) +{ + VkImageCreateInfo imageInfo = {}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = size.width(); + imageInfo.extent.height = size.height(); + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = format; + imageInfo.tiling = tiling; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = usage; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkImage image = VK_NULL_HANDLE; + + if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to create image!"); + return nullptr; + } + + QScopedPointer<VulkanImageWrapper> imageWrapper(new VulkanImageWrapper); + imageWrapper->textureImage = image; + imageWrapper->imgMemSize = memSize; + imageWrapper->imgSize = size; + + VkMemoryRequirements memRequirements; + vkGetImageMemoryRequirements(m_device, image, &memRequirements); + + VkExportMemoryAllocateInfoKHR exportAllocInfo = {}; + exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + int memoryType = findMemoryType(memRequirements.memoryTypeBits, properties); + if (memoryType < 0) + return nullptr; + allocInfo.memoryTypeIndex = memoryType; + allocInfo.pNext = &exportAllocInfo; + + if (vkAllocateMemory(m_device, &allocInfo, nullptr, &imageWrapper->textureImageMemory) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to allocate image memory!"); + return nullptr; + } + + int res = vkBindImageMemory(m_device, image, imageWrapper->textureImageMemory, 0); + Q_UNUSED(res); + if (extraDebug) qDebug() << "vkBindImageMemory res" << res; + + VkMemoryGetFdInfoKHR memoryFdInfo = {}; + memoryFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + memoryFdInfo.memory = imageWrapper->textureImageMemory; + memoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + res = vkGetMemoryFdKHR(m_device, &memoryFdInfo, &imageWrapper->imgFd); + if (extraDebug) qDebug() << "vkGetMemoryFdKHR res" << res << "fd" << imageWrapper->imgFd; + + return imageWrapper.take(); +} + + +bool VulkanWrapperPrivate::transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout) +{ + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + qCritical("VulkanWrapper: unsupported layout transition!"); + return false; + } + + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + endSingleTimeCommands(commandBuffer); + return true; +} + +bool VulkanWrapperPrivate::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) +{ + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = size; + bufferInfo.usage = usage; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to create buffer!"); + return false; + } + + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements); + + VkMemoryAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); + + if (vkAllocateMemory(m_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to allocate buffer memory!"); + return false; + } + + vkBindBufferMemory(m_device, buffer, bufferMemory, 0); + return true; +} + + +VkCommandBuffer VulkanWrapperPrivate::beginSingleTimeCommands() +{ + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = m_commandPool; + allocInfo.commandBufferCount = 1; + + if (extraDebug) qDebug() << "allocating..."; + + VkCommandBuffer commandBuffer; + int res = vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer); + Q_UNUSED(res); + if (extraDebug) qDebug() << "vkAllocateCommandBuffers res" << res; + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + res = vkBeginCommandBuffer(commandBuffer, &beginInfo); + if (extraDebug) qDebug() << "BEGIN res" << res; + + return commandBuffer; +} + +void VulkanWrapperPrivate::endSingleTimeCommands(VkCommandBuffer commandBuffer) +{ + int res = vkEndCommandBuffer(commandBuffer); + Q_UNUSED(res); + if (extraDebug) qDebug() << "END res" << res; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(m_graphicsQueue); + + vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer); +} + +void VulkanWrapperPrivate::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) +{ + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkBufferImageCopy region = {}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = {0, 0, 0}; + region.imageExtent = { + width, + height, + 1 + }; + + vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + endSingleTimeCommands(commandBuffer); +} + +void VulkanWrapperPrivate::createCommandPool() +{ + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(m_physicalDevice); + + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; + + if (vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) { + m_initFailed = true; + qCritical("VulkanWrapperPrivate: could not create command pool"); + } +} + +QueueFamilyIndices VulkanWrapperPrivate::findQueueFamilies(VkPhysicalDevice device) +{ + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + if (extraDebug) qDebug() << "queueFamilyCount" << queueFamilyCount; + + + std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + +#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG + for (const auto& queueFamily : queueFamilies) { + qDebug() << "....q" << "count" << queueFamily.queueCount << queueFamily.timestampValidBits << hex << queueFamily.queueFlags; + } +#endif + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + break; + } + i++; + } + + return indices; +} + +bool VulkanWrapperPrivate::createLogicalDevice() +{ + QueueFamilyIndices indices = findQueueFamilies(m_physicalDevice); + + std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; + std::set<int> uniqueQueueFamilies = {indices.graphicsFamily}; //////, indices.presentFamily}; + + float queuePriority = 1.0f; + for (int queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures = {}; + + VkDeviceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + + createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + + createInfo.pEnabledFeatures = &deviceFeatures; + + if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to create logical device!"); + return false; + } + + vkGetDeviceQueue(m_device, indices.graphicsFamily, 0, &m_graphicsQueue); + return true; +} + +VulkanImageWrapper *VulkanWrapperPrivate::createTextureImage(const QImage &img) +{ + return createTextureImageFromData(img.constBits(), img.sizeInBytes(), img.size(), VK_FORMAT_R8G8B8A8_UNORM); +} + +VulkanImageWrapper *VulkanWrapperPrivate::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat) +{ + if (m_initFailed) + return nullptr; + + int texWidth = size.width(); + int texHeight = size.height(); + bool ok; + if (extraDebug) qDebug("image load %p %dx%d", pixels, texWidth, texHeight); + if (!pixels) { + qCritical("VulkanWrapper: failed to load texture image!"); + return nullptr; + } + + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + ok = createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); + + if (!ok) + return nullptr; + + void* data; + vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data); + if (extraDebug) qDebug() << "mapped" << data << bufferSize; + memcpy(data, pixels, static_cast<size_t>(bufferSize)); + vkUnmapMemory(m_device, stagingBufferMemory); + + if (extraDebug) qDebug() << "creating image..."; + + QScopedPointer<VulkanImageWrapper> imageWrapper(createImage(vkFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, bufferSize)); + if (imageWrapper.isNull()) + return nullptr; + + if (extraDebug) qDebug() << "transition..."; + + const VkImage textureImage = imageWrapper->textureImage; + + ok = transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + if (!ok) + return nullptr; + + if (extraDebug) qDebug() << "copyBufferToImage..."; + copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight)); + transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vkDestroyBuffer(m_device, stagingBuffer, nullptr); + vkFreeMemory(m_device, stagingBufferMemory, nullptr); + + return imageWrapper.take(); +} + +void VulkanWrapperPrivate::freeTextureImage(VulkanImageWrapper *imageWrapper) +{ + if (!imageWrapper) + return; + + //"To avoid leaking resources, the application must release ownership of the file descriptor using the close system call" + ::close(imageWrapper->imgFd); + + // clean up the image memory + vkDestroyImage(m_device, imageWrapper->textureImage, nullptr); + vkFreeMemory(m_device, imageWrapper->textureImageMemory, nullptr); +} + +VulkanWrapperPrivate::VulkanWrapperPrivate(QOpenGLContext *glContext) +{ + if (extraDebug) qDebug("Creating Vulkan instance"); + VkApplicationInfo applicationInfo = {}; + applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + applicationInfo.pNext = nullptr; + applicationInfo.pApplicationName = nullptr; + applicationInfo.applicationVersion = 0; + applicationInfo.pEngineName = nullptr; + applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + applicationInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5); + + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pNext = nullptr; + instanceCreateInfo.flags = 0; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + instanceCreateInfo.enabledLayerCount = 0; + instanceCreateInfo.ppEnabledLayerNames = nullptr; + instanceCreateInfo.enabledExtensionCount = 0; + instanceCreateInfo.ppEnabledExtensionNames = nullptr; + + auto f_glGetVkProcAddrNV = reinterpret_cast<PFNGLGETVKPROCADDRNVPROC>(glContext->getProcAddress("glGetVkProcAddrNV")); + + if (!f_glGetVkProcAddrNV) { + qCritical("VulkanWrapper: Could not find Vulkan/GL interop function glGetVkProcAddrNV"); + m_initFailed = true; + return; + } + + initFunctions(f_glGetVkProcAddrNV); + + VkResult instanceCreationResult = vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance); + + if (extraDebug) qDebug() << "result" << instanceCreationResult; + + if (instanceCreationResult != VK_SUCCESS) { + qCritical() << "VulkanWrapper: Failed to create Vulkan instance: Error " + << instanceCreationResult; + m_initFailed = true; + return; + } + + uint32_t devCount; + + auto res = vkEnumeratePhysicalDevices(m_instance, &devCount, nullptr); + if (extraDebug) qDebug() << "vkEnumeratePhysicalDevices res =" << res << "count =" << devCount; + + QVarLengthArray<VkPhysicalDevice, 5> dev(devCount); + + res = vkEnumeratePhysicalDevices(m_instance, &devCount, dev.data()); + if (extraDebug) qDebug() << "...devs res =" << res << "count =" << devCount; + +#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG + VkPhysicalDeviceProperties props; + + vkGetPhysicalDeviceProperties(dev[0], &props); + + qDebug() << "Properties " << hex + << "apiVersion" << props.apiVersion + << "driverVersion" << props.driverVersion + << "vendorID" << props.vendorID + << "deviceID" << props.deviceID + << "deviceType" << props.deviceType + << "deviceName" << props.deviceName; +#endif + + m_physicalDevice = dev[0]; //TODO handle the case of multiple GPUs where only some support Vulkan + + bool ok = createLogicalDevice(); + if (!ok) { + qCritical("VulkanWrapperPrivate: could not create logical device"); + m_initFailed = true; + return; + } + + VkPhysicalDeviceMemoryProperties memProps; + + + vkGetPhysicalDeviceMemoryProperties(dev[0], &memProps); + +#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG + qDebug() << "Physical memory properties:\n" << "types:" << memProps.memoryTypeCount << "heaps:" << memProps.memoryHeapCount; + for (uint i = 0; i < memProps.memoryTypeCount; ++i) + qDebug() << " " << i << "heap" << memProps.memoryTypes[i].heapIndex << "flags" << hex << memProps.memoryTypes[i].propertyFlags; + + for (uint i = 0; i < memProps.memoryHeapCount; ++i) + qDebug() << " " << i << "size" << memProps.memoryHeaps[i].size << "flags" << hex << memProps.memoryHeaps[i].flags; +#endif + + int gpuMemoryType = -1; + + for (uint i = 0; i < memProps.memoryTypeCount; ++i) { + if (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + gpuMemoryType = i; + break; + } + } + + if (gpuMemoryType < 0) { + qCritical("VulkanWrapper: Could not find GPU memory!"); + m_initFailed = true; + return; + } + +#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG + qDebug() << "GPU memory type:" << gpuMemoryType << "heap:" << memProps.memoryTypes[gpuMemoryType].heapIndex; + + for (int f = 0; f <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; f++) + { + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(dev[0], VkFormat(f), &formatProps); + qDebug() << "format" << f << "features" << hex << formatProps.linearTilingFeatures << formatProps.optimalTilingFeatures << formatProps.bufferFeatures; + } +#endif + createCommandPool(); +} + + +VulkanWrapper::VulkanWrapper(QOpenGLContext *glContext) + : d_ptr(new VulkanWrapperPrivate(glContext)) +{ +} + +VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &img) +{ + return d_ptr->createTextureImage(img); +} + +VulkanImageWrapper *VulkanWrapper::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat) +{ + VkFormat vkFormat = VkFormat(QVkConvenience::vkFormatFromGlFormat(glInternalFormat)); + + if (vkFormat == VK_FORMAT_UNDEFINED) + return nullptr; + + return d_ptr->createTextureImageFromData(pixels, bufferSize, size, vkFormat); +} + +int VulkanWrapper::getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w, int *h) +{ + if (memSize) + *memSize = imgWrapper->imgMemSize; + if (w) + *w = imgWrapper->imgSize.width(); + if (h) + *h = imgWrapper->imgSize.height(); + return imgWrapper->imgFd; +} + +void VulkanWrapper::freeTextureImage(VulkanImageWrapper *imageWrapper) +{ + d_ptr->freeTextureImage(imageWrapper); +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h new file mode 100644 index 000000000..541618fb3 --- /dev/null +++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VULKANWRAPPER_H +#define VULKANWRAPPER_H + +#include <QOpenGLContext> + +QT_BEGIN_NAMESPACE + +class VulkanWrapper; +struct VulkanImageWrapper; +class VulkanWrapperPrivate; + +class QOpenGLContext; +class QImage; + +class VulkanWrapper +{ +public: + VulkanWrapper(QOpenGLContext *glContext); + + VulkanImageWrapper *createTextureImage(const QImage &img); + VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat); + int getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w = nullptr, int *h = nullptr); + void freeTextureImage(VulkanImageWrapper *imageWrapper); + +private: + VulkanWrapperPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // VULKANWRAPPER_H |