From 43d8a3091894ceb4ab934167b2f3eda27564eb6d Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 21 Jan 2019 10:06:49 +0100 Subject: 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 Reviewed-by: Johan Helsing --- config.tests/vulkan_server_buffer/main.cpp | 60 ++ .../vulkan_server_buffer/vulkan_server_buffer.pro | 1 + src/client/configure.json | 17 + .../qwaylandserverbufferintegration_p.h | 3 +- src/compositor/configure.json | 15 + src/compositor/extensions/extensions.pri | 5 + .../extensions/qwltexturesharingextension.cpp | 497 ++++++++++++++ .../extensions/qwltexturesharingextension_p.h | 159 +++++ .../qwlserverbufferintegration_p.h | 19 +- src/extensions/qt-texture-sharing-unstable-v1.xml | 57 ++ .../qt-vulkan-server-buffer-unstable-v1.xml | 64 ++ .../client/vulkan-server/vulkan-server.pri | 12 + .../vulkanserverbufferintegration.cpp | 216 ++++++ .../vulkan-server/vulkanserverbufferintegration.h | 102 +++ .../compositor/vulkan-server/vulkan-server.pri | 16 + .../vulkanserverbufferintegration.cpp | 325 +++++++++ .../vulkan-server/vulkanserverbufferintegration.h | 111 ++++ .../compositor/vulkan-server/vulkanwrapper.cpp | 733 +++++++++++++++++++++ .../compositor/vulkan-server/vulkanwrapper.h | 70 ++ src/imports/imports.pro | 7 +- src/imports/texture-sharing-extension/plugin.cpp | 109 +++ src/imports/texture-sharing-extension/qmldir | 3 + .../texture-sharing-extension.pro | 13 + src/imports/texture-sharing/plugin.cpp | 97 +++ src/imports/texture-sharing/qmldir | 3 + .../texture-sharing/sharedtextureprovider.cpp | 322 +++++++++ .../texture-sharing/sharedtextureprovider.h | 118 ++++ src/imports/texture-sharing/texture-sharing.pro | 23 + .../texture-sharing/texturesharingextension.cpp | 86 +++ .../texture-sharing/texturesharingextension.h | 77 +++ src/plugins/hardwareintegration/client/client.pro | 2 + .../client/vulkan-server/main.cpp | 66 ++ .../client/vulkan-server/vulkan-server.json | 3 + .../client/vulkan-server/vulkan-server.pro | 15 + .../hardwareintegration/compositor/compositor.pro | 2 + .../compositor/vulkan-server/main.cpp | 62 ++ .../compositor/vulkan-server/vulkan-server.json | 3 + .../compositor/vulkan-server/vulkan-server.pro | 12 + src/src.pro | 2 +- sync.profile | 2 + 40 files changed, 3502 insertions(+), 7 deletions(-) create mode 100644 config.tests/vulkan_server_buffer/main.cpp create mode 100644 config.tests/vulkan_server_buffer/vulkan_server_buffer.pro create mode 100644 src/compositor/extensions/qwltexturesharingextension.cpp create mode 100644 src/compositor/extensions/qwltexturesharingextension_p.h create mode 100644 src/extensions/qt-texture-sharing-unstable-v1.xml create mode 100644 src/extensions/qt-vulkan-server-buffer-unstable-v1.xml create mode 100644 src/hardwareintegration/client/vulkan-server/vulkan-server.pri create mode 100644 src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp create mode 100644 src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h create mode 100644 src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri create mode 100644 src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp create mode 100644 src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h create mode 100644 src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp create mode 100644 src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h create mode 100644 src/imports/texture-sharing-extension/plugin.cpp create mode 100644 src/imports/texture-sharing-extension/qmldir create mode 100644 src/imports/texture-sharing-extension/texture-sharing-extension.pro create mode 100644 src/imports/texture-sharing/plugin.cpp create mode 100644 src/imports/texture-sharing/qmldir create mode 100644 src/imports/texture-sharing/sharedtextureprovider.cpp create mode 100644 src/imports/texture-sharing/sharedtextureprovider.h create mode 100644 src/imports/texture-sharing/texture-sharing.pro create mode 100644 src/imports/texture-sharing/texturesharingextension.cpp create mode 100644 src/imports/texture-sharing/texturesharingextension.h create mode 100644 src/plugins/hardwareintegration/client/vulkan-server/main.cpp create mode 100644 src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json create mode 100644 src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro create mode 100644 src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp create mode 100644 src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json create mode 100644 src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro diff --git a/config.tests/vulkan_server_buffer/main.cpp b/config.tests/vulkan_server_buffer/main.cpp new file mode 100644 index 000000000..5bd88d338 --- /dev/null +++ b/config.tests/vulkan_server_buffer/main.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Compositor. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + VkExportMemoryAllocateInfoKHR exportAllocInfo = {}; + exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + return 0; +} diff --git a/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro b/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro new file mode 100644 index 000000000..28dcadcbf --- /dev/null +++ b/config.tests/vulkan_server_buffer/vulkan_server_buffer.pro @@ -0,0 +1 @@ +SOURCES += main.cpp diff --git a/src/client/configure.json b/src/client/configure.json index 586da6f66..06fa87757 100644 --- a/src/client/configure.json +++ b/src/client/configure.json @@ -75,6 +75,11 @@ "type": "compile", "test": "dmabuf_server_buffer", "use": "egl" + }, + "vulkan-server-buffer": { + "label": "Vulkan Buffer Sharing", + "type": "compile", + "test": "vulkan_server_buffer" } }, @@ -123,6 +128,16 @@ "condition": "features.wayland-client && features.opengl && features.egl && tests.dmabuf-server-buffer", "output": [ "privateFeature" ] }, + "wayland-client-texture-sharing-experimental" : { + "label": "Texture sharing (experimental)", + "autoDetect": "false", + "output": [ "privateFeature" ] + }, + "wayland-vulkan-server-buffer": { + "label": "Vulkan-based server buffer integration", + "condition": "features.wayland-client && features.opengl && features.egl && tests.vulkan-server-buffer && features.wayland-client-texture-sharing-experimental", + "output": [ "privateFeature" ] + }, "wayland-shm-emulation-server-buffer": { "label": "Shm emulation server buffer integration", "condition": "features.wayland-client && features.opengl", @@ -149,6 +164,8 @@ "xcomposite-glx", "wayland-drm-egl-server-buffer", "wayland-libhybris-egl-server-buffer", + "wayland-dmabuf-server-buffer", + "wayland-vulkan-server-buffer", "wayland-shm-emulation-server-buffer" ] }, diff --git a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h index 7439087d8..719dad9ed 100644 --- a/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h +++ b/src/client/hardwareintegration/qwaylandserverbufferintegration_p.h @@ -70,7 +70,8 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandServerBuffer public: enum Format { RGBA32, - A8 + A8, + Custom }; QWaylandServerBuffer(); diff --git a/src/compositor/configure.json b/src/compositor/configure.json index 9a6655241..0412d5e3b 100644 --- a/src/compositor/configure.json +++ b/src/compositor/configure.json @@ -80,6 +80,11 @@ "type": "compile", "test": "dmabuf_server_buffer", "use": "egl" + }, + "vulkan-server-buffer": { + "label": "Vulkan Buffer Sharing", + "type": "compile", + "test": "vulkan_server_buffer" } }, @@ -128,6 +133,16 @@ "condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-server-buffer", "output": [ "privateFeature" ] }, + "wayland-compositor-texture-sharing-experimental" : { + "label": "Texture sharing (experimental)", + "autoDetect": "false", + "output": [ "privateFeature" ] + }, + "wayland-vulkan-server-buffer": { + "label": "Vulkan-based server buffer integration", + "condition": "features.wayland-server && features.opengl && features.egl && tests.vulkan-server-buffer && features.wayland-compositor-texture-sharing-experimental", + "output": [ "privateFeature" ] + }, "wayland-shm-emulation-server-buffer": { "label": "Shm emulation server buffer", "condition": "features.wayland-server && features.opengl", diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 5c708f891..3ca575cac 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -75,6 +75,11 @@ qtHaveModule(quick):contains(QT_CONFIG, opengl) { extensions/qwaylandxdgshellv6integration.cpp \ extensions/qwaylandxdgshellintegration.cpp + qtConfig(wayland-compositor-texture-sharing-experimental) { + HEADERS += extensions/qwltexturesharingextension_p.h + SOURCES += extensions/qwltexturesharingextension.cpp + WAYLANDSERVERSOURCES += ../extensions/qt-texture-sharing-unstable-v1.xml + } } include ($$PWD/pregenerated/xdg-shell-v5.pri) diff --git a/src/compositor/extensions/qwltexturesharingextension.cpp b/src/compositor/extensions/qwltexturesharingextension.cpp new file mode 100644 index 000000000..251c5fec0 --- /dev/null +++ b/src/compositor/extensions/qwltexturesharingextension.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "qwltexturesharingextension_p.h" + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class SharedTexture : public QSGTexture +{ + Q_OBJECT +public: + SharedTexture(QtWayland::ServerBuffer *buffer); + + int textureId() const override; + QSize textureSize() const override; + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + + void bind() override; + +private: + void updateGLTexture() const; + QtWayland::ServerBuffer *m_buffer = nullptr; + mutable QOpenGLTexture *m_tex = nullptr; +}; + +SharedTexture::SharedTexture(QtWayland::ServerBuffer *buffer) + : m_buffer(buffer), m_tex(nullptr) +{ +} + +int SharedTexture::textureId() const +{ + updateGLTexture(); + return m_tex ? m_tex->textureId() : 0; +} + +QSize SharedTexture::textureSize() const +{ + updateGLTexture(); + return m_tex ? QSize(m_tex->width(), m_tex->height()) : QSize(); +} + +bool SharedTexture::hasAlphaChannel() const +{ + return true; +} + +bool SharedTexture::hasMipmaps() const +{ + updateGLTexture(); + return m_tex ? (m_tex->mipLevels() > 1) : false; +} + +void SharedTexture::bind() +{ + updateGLTexture(); + if (m_tex) + m_tex->bind(); +} + +inline void SharedTexture::updateGLTexture() const +{ + if (!m_tex && m_buffer) + m_tex = m_buffer->toOpenGlTexture(); +} + +class SharedTextureFactory : public QQuickTextureFactory +{ +public: + SharedTextureFactory(const QtWayland::ServerBuffer *buffer) + : m_buffer(buffer) + { + } + + ~SharedTextureFactory() override + { + if (m_buffer && !QCoreApplication::closingDown()) + const_cast(m_buffer)->releaseOpenGlTexture(); + } + + QSize textureSize() const override + { + return m_buffer ? m_buffer->size() : QSize(); + } + + int textureByteCount() const override + { + return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0; + } + + QSGTexture *createTexture(QQuickWindow *) const override + { + return new SharedTexture(const_cast(m_buffer)); + } + +private: + const QtWayland::ServerBuffer *m_buffer = nullptr; +}; + +class SharedTextureImageResponse : public QQuickImageResponse +{ + Q_OBJECT +public: + SharedTextureImageResponse(QWaylandTextureSharingExtension *extension, const QString &id) + : m_id(id) + { + if (extension) + doRequest(extension); + } + + void doRequest(QWaylandTextureSharingExtension *extension) + { + m_extension = extension; + connect(extension, &QWaylandTextureSharingExtension::bufferResult, this, &SharedTextureImageResponse::doResponse); + QMetaObject::invokeMethod(extension, [this] { m_extension->requestBuffer(m_id); }, Qt::AutoConnection); + } + + QQuickTextureFactory *textureFactory() const override + { + if (m_buffer) { +// qDebug() << "Creating shared buffer texture for" << m_id; + return new SharedTextureFactory(m_buffer); + } +// qDebug() << "Shared buffer NOT found for" << m_id; + m_errorString = QLatin1Literal("Shared buffer not found"); + return nullptr; + } + + QString errorString() const override + { + return m_errorString; + } + +public slots: + void doResponse(const QString &key, QtWayland::ServerBuffer *buffer) + { + if (key != m_id) + return; //somebody else's texture + + m_buffer = buffer; + + if (m_extension) + disconnect(m_extension, &QWaylandTextureSharingExtension::bufferResult, this, &SharedTextureImageResponse::doResponse); + + emit finished(); + } + +private: + QString m_id; + QWaylandTextureSharingExtension *m_extension = nullptr; + mutable QString m_errorString; + QtWayland::ServerBuffer *m_buffer = nullptr; +}; + +QWaylandSharedTextureProvider::QWaylandSharedTextureProvider() +{ +} + +QWaylandSharedTextureProvider::~QWaylandSharedTextureProvider() +{ +} + +QQuickImageResponse *QWaylandSharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize) +{ + Q_UNUSED(requestedSize); + +// qDebug() << "Provider: got request for" << id; + + auto *extension = QWaylandTextureSharingExtension::self(); + auto *response = new SharedTextureImageResponse(extension, id); + if (!extension) + m_pendingResponses << response; + + return response; +} + +void QWaylandSharedTextureProvider::setExtensionReady(QWaylandTextureSharingExtension *extension) +{ + for (auto *response : qAsConst(m_pendingResponses)) + response->doRequest(extension); + m_pendingResponses.clear(); + m_pendingResponses.squeeze(); +} + +QWaylandTextureSharingExtension *QWaylandTextureSharingExtension::s_self = nullptr; // theoretical race conditions, but OK as long as we don't delete it while we are running + +QWaylandTextureSharingExtension::QWaylandTextureSharingExtension() +{ + s_self = this; +} + +QWaylandTextureSharingExtension::QWaylandTextureSharingExtension(QWaylandCompositor *compositor) + :QWaylandCompositorExtensionTemplate(compositor) +{ + s_self = this; +} + +QWaylandTextureSharingExtension::~QWaylandTextureSharingExtension() +{ + //qDebug() << Q_FUNC_INFO; + //dumpBufferInfo(); + + for (auto b : m_server_buffers) + delete b.buffer; + + if (s_self == this) + s_self = nullptr; +} + +void QWaylandTextureSharingExtension::setImageSearchPath(const QString &path) +{ + m_image_dirs = path.split(QLatin1Char(';')); + + for (auto it = m_image_dirs.begin(); it != m_image_dirs.end(); ++it) + if (!(*it).endsWith(QLatin1Char('/'))) + (*it) += QLatin1Char('/'); +} + +void QWaylandTextureSharingExtension::initialize() +{ + QWaylandCompositorExtensionTemplate::initialize(); + QWaylandCompositor *compositor = static_cast(extensionContainer()); + init(compositor->display(), 1); + + QString image_search_path = qEnvironmentVariable("QT_WAYLAND_SHAREDTEXTURE_SEARCH_PATH"); + if (!image_search_path.isEmpty()) + setImageSearchPath(image_search_path); + + if (m_image_dirs.isEmpty()) + m_image_dirs << QLatin1Literal(":/") << QLatin1Literal("./"); + + auto suffixes = QTextureFileReader::supportedFileFormats(); + suffixes.append(QImageReader::supportedImageFormats()); + for (auto ext : qAsConst(suffixes)) + m_image_suffixes << QLatin1Char('.') + QString::fromLatin1(ext); + + //qDebug() << "m_image_suffixes" << m_image_suffixes << "m_image_dirs" << m_image_dirs; + + auto *ctx = QQmlEngine::contextForObject(this); + if (ctx) { + QQmlEngine *engine = ctx->engine(); + if (engine) { + auto *provider = static_cast(engine->imageProvider(QLatin1Literal("wlshared"))); + if (provider) + provider->setExtensionReady(this); + } + } +} + +QString QWaylandTextureSharingExtension::getExistingFilePath(const QString &key) const +{ + // The default search path blocks absolute pathnames, but this does not prevent relative + // paths containing '../'. We handle that here, at the price of also blocking directory + // names ending with two or more dots. + + if (key.contains(QLatin1Literal("../"))) + return QString(); + + for (auto dir : m_image_dirs) { + QString path = dir + key; + if (QFileInfo::exists(path)) + return path; + } + + for (auto dir : m_image_dirs) { + for (auto ext : m_image_suffixes) { + QString fp = dir + key + ext; + //qDebug() << "trying" << fp; + if (QFileInfo::exists(fp)) + return fp; + } + } + return QString(); +} + +QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getBuffer(const QString &key) +{ + if (!initServerBufferIntegration()) + return nullptr; + +//qDebug() << "getBuffer" << key; + + QtWayland::ServerBuffer *buffer = nullptr; + + if ((buffer = m_server_buffers.value(key).buffer)) + return buffer; + + QByteArray pixelData; + QSize size; + uint glInternalFormat = GL_NONE; + + if (customPixelData(key, &pixelData, &size, &glInternalFormat)) { + if (!pixelData.isEmpty()) { + buffer = m_server_buffer_integration->createServerBufferFromData(pixelData, size, glInternalFormat); + if (!buffer) + qWarning() << "QWaylandTextureSharingExtension: could not create buffer from custom data for key:" << key; + } + } else { + QString pathName = getExistingFilePath(key); + //qDebug() << "pathName" << pathName; + if (pathName.isEmpty()) + return nullptr; + + buffer = getCompressedBuffer(pathName); + //qDebug() << "getCompressedBuffer" << buffer; + + if (!buffer) { + QImage img(pathName); + if (!img.isNull()) { + img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + buffer = m_server_buffer_integration->createServerBufferFromImage(img, QtWayland::ServerBuffer::RGBA32); + } + //qDebug() << "createServerBufferFromImage" << buffer; + } + } + if (buffer) + m_server_buffers.insert(key, BufferInfo(buffer)); + + //qDebug() << ">>>>" << key << buffer; + + return buffer; +} + +// Compositor requesting image for its own UI +void QWaylandTextureSharingExtension::requestBuffer(const QString &key) +{ + //qDebug() << "requestBuffer" << key; + + if (thread() != QThread::currentThread()) + qWarning("QWaylandTextureSharingExtension::requestBuffer() called from outside main thread: possible race condition"); + + auto *buffer = getBuffer(key); + + if (buffer) + m_server_buffers[key].usedLocally = true; + + //dumpBufferInfo(); + + emit bufferResult(key, buffer); +} + +void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_request_image(Resource *resource, const QString &key) +{ + //qDebug() << "texture_sharing_request_image" << key; + auto *buffer = getBuffer(key); + if (buffer) { + struct ::wl_client *client = resource->client(); + struct ::wl_resource *buffer_resource = buffer->resourceForClient(client); + //qDebug() << " server_buffer resource" << buffer_resource; + if (buffer_resource) + send_provide_buffer(resource->handle, buffer_resource, key); + else + qWarning() << "QWaylandTextureSharingExtension: no buffer resource for client"; + } else { + send_image_failed(resource->handle, key, QString()); + } + //dumpBufferInfo(); +} + +void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_abandon_image(Resource *resource, const QString &key) +{ + Q_UNUSED(resource); + Q_UNUSED(key); +// qDebug() << Q_FUNC_INFO << resource << key; + QTimer::singleShot(100, this, &QWaylandTextureSharingExtension::cleanupBuffers); +} + +// A client has disconnected +void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); +// qDebug() << "texture_sharing_destroy_resource" << resource->handle << resource->handle->object.id << "client" << resource->client(); +// dumpBufferInfo(); + QTimer::singleShot(1000, this, &QWaylandTextureSharingExtension::cleanupBuffers); +} + +bool QWaylandTextureSharingExtension::initServerBufferIntegration() +{ + if (!m_server_buffer_integration) { + QWaylandCompositor *compositor = static_cast(extensionContainer()); + + m_server_buffer_integration = QWaylandCompositorPrivate::get(compositor)->serverBufferIntegration(); + if (!m_server_buffer_integration) { + qWarning("QWaylandTextureSharingExtension initialization failed: No Server Buffer Integration"); + if (qEnvironmentVariableIsEmpty("QT_WAYLAND_SERVER_BUFFER_INTEGRATION")) + qWarning("Set the environment variable 'QT_WAYLAND_SERVER_BUFFER_INTEGRATION' to specify."); + return false; + } + } + return true; +} + +QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getCompressedBuffer(const QString &pathName) +{ + QFile f(pathName); + if (!f.open(QIODevice::ReadOnly)) + return nullptr; + + QTextureFileReader r(&f, pathName); + + if (!r.canRead()) + return nullptr; + + QTextureFileData td(r.read()); + + //qDebug() << "QWaylandTextureSharingExtension: reading compressed texture data" << td; + + if (!td.isValid()) { + qWarning() << "VulkanServerBufferIntegration:" << pathName << "not valid compressed texture"; + return nullptr; + } + + QByteArray pixelData = QByteArray::fromRawData(td.data().constData() + td.dataOffset(), td.dataLength()); + + return m_server_buffer_integration->createServerBufferFromData(pixelData, td.size(), td.glInternalFormat()); +} + +void QWaylandTextureSharingExtension::cleanupBuffers() +{ + for (auto it = m_server_buffers.begin(); it != m_server_buffers.end(); ) { + auto *buffer = it.value().buffer; + if (!it.value().usedLocally && !buffer->bufferInUse()) { + //qDebug() << "deleting buffer for" << it.key(); + it = m_server_buffers.erase(it); + delete buffer; + } else { + ++it; + } + } + //dumpBufferInfo(); +} + +void QWaylandTextureSharingExtension::dumpBufferInfo() +{ + qDebug() << "shared buffers:" << m_server_buffers.count(); + for (auto it = m_server_buffers.cbegin(); it != m_server_buffers.cend(); ++it) + qDebug() << " " << it.key() << ":" << it.value().buffer << "in use" << it.value().buffer->bufferInUse() << "usedLocally" << it.value().usedLocally ; +} + +QT_END_NAMESPACE + +#include "qwltexturesharingextension.moc" diff --git a/src/compositor/extensions/qwltexturesharingextension_p.h b/src/compositor/extensions/qwltexturesharingextension_p.h new file mode 100644 index 000000000..8f442a200 --- /dev/null +++ b/src/compositor/extensions/qwltexturesharingextension_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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 QWLTEXTURESHARINGEXTENSION_P_H +#define QWLTEXTURESHARINGEXTENSION_P_H + +// +// 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. +// + +#include "wayland-util.h" + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace QtWayland +{ + class ServerBufferIntegration; +} + +class QWaylandTextureSharingExtension; +class SharedTextureImageResponse; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSharedTextureProvider : public QQuickAsyncImageProvider +{ +public: + QWaylandSharedTextureProvider(); + ~QWaylandSharedTextureProvider() override; + + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; + void setExtensionReady(QWaylandTextureSharingExtension *extension); + +private: + QVector m_pendingResponses; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextureSharingExtension + : public QWaylandCompositorExtensionTemplate + , public QtWaylandServer::zqt_texture_sharing_v1 +{ + Q_OBJECT + Q_PROPERTY(QString imageSearchPath WRITE setImageSearchPath) +public: + QWaylandTextureSharingExtension(); + QWaylandTextureSharingExtension(QWaylandCompositor *compositor); + ~QWaylandTextureSharingExtension() override; + + void initialize() override; + + void setImageSearchPath(const QString &path); + + static QWaylandTextureSharingExtension *self() { return s_self; } + +public slots: + void requestBuffer(const QString &key); + +signals: + void bufferResult(const QString &key, QtWayland::ServerBuffer *buffer); + +protected slots: + void cleanupBuffers(); + +protected: + void zqt_texture_sharing_v1_request_image(Resource *resource, const QString &key) override; + void zqt_texture_sharing_v1_abandon_image(Resource *resource, const QString &key) override; + void zqt_texture_sharing_v1_destroy_resource(Resource *resource) override; + + virtual bool customPixelData(const QString &key, QByteArray *data, QSize *size, uint *glInternalFormat) + { + Q_UNUSED(key); + Q_UNUSED(data); + Q_UNUSED(size); + Q_UNUSED(glInternalFormat); + return false; + } + +private: + QtWayland::ServerBuffer *getBuffer(const QString &key); + bool initServerBufferIntegration(); + QtWayland::ServerBuffer *getCompressedBuffer(const QString &key); + QString getExistingFilePath(const QString &key) const; + void dumpBufferInfo(); + + struct BufferInfo + { + BufferInfo(QtWayland::ServerBuffer *b = nullptr) : buffer(b) {} + QtWayland::ServerBuffer *buffer = nullptr; + bool usedLocally = false; + }; + + QStringList m_image_dirs; + QStringList m_image_suffixes; + QHash m_server_buffers; + QtWayland::ServerBufferIntegration *m_server_buffer_integration = nullptr; + + static QWaylandTextureSharingExtension *s_self; +}; + +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandTextureSharingExtension) + +QT_END_NAMESPACE + +#endif // QWLTEXTURESHARINGEXTENSION_P_H diff --git a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h index 7d8901d58..b8d9fa99d 100644 --- a/src/compositor/hardware_integration/qwlserverbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlserverbufferintegration_p.h @@ -55,8 +55,7 @@ #include #include -#include - +#include struct wl_client; struct wl_resource; @@ -75,7 +74,8 @@ class Q_WAYLAND_COMPOSITOR_EXPORT ServerBuffer public: enum Format { RGBA32, - A8 + A8, + Custom }; ServerBuffer(const QSize &size, ServerBuffer::Format format); @@ -85,7 +85,9 @@ public: virtual bool bufferInUse() { return true; } virtual QOpenGLTexture *toOpenGlTexture() = 0; - +#if QT_CONFIG(wayland_compositor_texture_sharing_experimental) + virtual void releaseOpenGlTexture() {} +#endif virtual bool isYInverted() const; QSize size() const; @@ -105,6 +107,15 @@ public: virtual bool supportsFormat(ServerBuffer::Format format) const = 0; virtual ServerBuffer *createServerBufferFromImage(const QImage &qimage, ServerBuffer::Format format) = 0; +#if QT_CONFIG(wayland_compositor_texture_sharing_experimental) + virtual ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) + { + Q_UNUSED(data); + Q_UNUSED(size); + Q_UNUSED(glInternalFormat); + return nullptr; + } +#endif }; } diff --git a/src/extensions/qt-texture-sharing-unstable-v1.xml b/src/extensions/qt-texture-sharing-unstable-v1.xml new file mode 100644 index 000000000..262ae487c --- /dev/null +++ b/src/extensions/qt-texture-sharing-unstable-v1.xml @@ -0,0 +1,57 @@ + + + + Copyright (C) 2019 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + This file is part of the plugins of the Qt Toolkit. + + $QT_BEGIN_LICENSE:BSD$ + You may use this file under the terms of the BSD license as follows: + + "Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Qt Company Ltd nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + + $QT_END_LICENSE$ + + + + + + + + + + + + + + + + + + + diff --git a/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml b/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml new file mode 100644 index 000000000..211d0a7c7 --- /dev/null +++ b/src/extensions/qt-vulkan-server-buffer-unstable-v1.xml @@ -0,0 +1,64 @@ + + + + Copyright (C) 2019 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + This file is part of the plugins of the Qt Toolkit. + + $QT_BEGIN_LICENSE:BSD$ + You may use this file under the terms of the BSD license as follows: + + "Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Qt Company Ltd nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + + $QT_END_LICENSE$ + + + + This protocol is used internally by Qt for implementing the + qt_server_buffer extension on hardware that supports Vulkan external memory . + + This protocol is not part of the Qt API. It exists purely as an + implementation detail and may change from version to + version without notice, or even be removed. + + + + Informs the client about a newly created server buffer. + The "fd" argument is a POSIX file descriptor representing the + underlying resources of a Vulkan device memory object as defined + in the GL_EXT_memory_object_fd extension. + + + + + + + + + + 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 +#include + +#include "vulkanserverbufferintegration.h" +#include +#include +#include +#include +#include +#include + +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(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(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(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 +#include "qwayland-qt-vulkan-server-buffer-unstable-v1.h" +#include + +#include "vulkanserverbufferintegration.h" +#include +#include + +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 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 +#include + +#include "vulkanserverbufferintegration.h" + +#include "vulkanwrapper.h" + +#include +#include +#include + +#include +#include + +#include + +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(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(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 + +#include "qwayland-server-qt-vulkan-server-buffer-unstable-v1.h" + +#include +#include +#include +#include + +#include +#include + +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 +#include +#include + +#include "vulkanwrapper.h" + +#include +#include + +#include + +#include + +#include + +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(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 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 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 queueCreateInfos; + std::set 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(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(bufferSize)); + vkUnmapMemory(m_device, stagingBufferMemory); + + if (extraDebug) qDebug() << "creating image..."; + + QScopedPointer 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(texWidth), static_cast(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(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 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 + +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 diff --git a/src/imports/imports.pro b/src/imports/imports.pro index c57c95d20..c8394f0c1 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -1,3 +1,8 @@ TEMPLATE = subdirs -qtHaveModule(quick): SUBDIRS += compositor +qtHaveModule(quick): { + SUBDIRS += \ + compositor \ + texture-sharing \ + texture-sharing-extension +} diff --git a/src/imports/texture-sharing-extension/plugin.cpp b/src/imports/texture-sharing-extension/plugin.cpp new file mode 100644 index 000000000..42dcd8e2d --- /dev/null +++ b/src/imports/texture-sharing-extension/plugin.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include +#include + +#include "QtWaylandCompositor/private/qwltexturesharingextension_p.h" + +/*! + \qmlmodule QtWayland.Compositor.TextureSharingExtension 1 + \title Qt Wayland Shared Texture Provider + \ingroup qmlmodules + \brief Adds a mechanism to share GPU memory + + \section2 Summary + + This module lets the compositor export graphical resources that can be used by clients, + without allocating any graphics memory in the client. + + \section2 Usage + + This module is imported like this: + + \code + import QtWayland.Compositor.TextureSharingExtension 1.0 + \endcode + + To use this module in a compositor, instantiate the extension object as a child of the compositor object, like this: + + + \code + WaylandCompositor { + //... + TextureSharingExtension { + } + } + \endcode + + The sharing functionality is provided through a QQuickImageProvider. Use + the "image:" scheme for the URL source of the image, followed by the + identifier \e wlshared, followed by the image file path. For example: + + \code + Image { source: "image://wlshared/wallpapers/mybackground.jpg" } + \endcode + +*/ + +QT_BEGIN_NAMESPACE + +class QWaylandTextureSharingExtensionPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + QWaylandTextureSharingExtensionPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) {} + + void registerTypes(const char *uri) override + { + Q_ASSERT(uri == QStringLiteral("QtWayland.Compositor.TextureSharingExtension")); + qmlRegisterType("QtWayland.Compositor.TextureSharingExtension", 1, 0, "TextureSharingExtension"); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + Q_UNUSED(uri); + engine->addImageProvider("wlshared", new QWaylandSharedTextureProvider); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/texture-sharing-extension/qmldir b/src/imports/texture-sharing-extension/qmldir new file mode 100644 index 000000000..182e5c0ee --- /dev/null +++ b/src/imports/texture-sharing-extension/qmldir @@ -0,0 +1,3 @@ +module QtWayland.Compositor.TextureSharingExtension +plugin qwaylandtexturesharingextension +classname QWaylandTextureSharingExtensionPlugin diff --git a/src/imports/texture-sharing-extension/texture-sharing-extension.pro b/src/imports/texture-sharing-extension/texture-sharing-extension.pro new file mode 100644 index 000000000..577ab58e7 --- /dev/null +++ b/src/imports/texture-sharing-extension/texture-sharing-extension.pro @@ -0,0 +1,13 @@ +CXX_MODULE = qml +TARGET = qwaylandtexturesharingextension +TARGETPATH = QtWayland/Compositor/TextureSharingExtension +IMPORT_VERSION = 1.$$QT_MINOR_VERSION + +SOURCES += \ + plugin.cpp + +QT += quick-private qml gui-private core-private waylandcompositor waylandcompositor-private + +requires(qtConfig(wayland-compositor-texture-sharing-experimental)) + +load(qml_plugin) diff --git a/src/imports/texture-sharing/plugin.cpp b/src/imports/texture-sharing/plugin.cpp new file mode 100644 index 000000000..9cf6bbca1 --- /dev/null +++ b/src/imports/texture-sharing/plugin.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandClient 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$ +** +****************************************************************************/ + +#include +#include + +#include "sharedtextureprovider.h" + +/*! + \qmlmodule QtWayland.Client.TextureSharing 1 + \title Qt Wayland Shared Texture Provider + \ingroup qmlmodules + \brief Adds an image provider which utilizes shared GPU memory + + \section2 Summary + + This module allows Qt Wayland clients to use graphical resources exported + by the compositor, without allocating any graphics memory in the client. + \section2 Usage + + To use this module, import it like this: + \code + import QtWayland.Client.TextureSharing 1.0 + \endcode + + The sharing functionality is provided through a QQuickImageProvider. Use + the "image:" scheme for the URL source of the image, followed by the + identifier \e wlshared, followed by the image file path. For example: + + \code + Image { source: "image://wlshared/wallpapers/mybackground.jpg" } + \endcode + + The shared texture module does not provide any directly usable QML types. +*/ + +QT_BEGIN_NAMESPACE + +class QWaylandTextureSharingPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + QWaylandTextureSharingPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) {} + + void registerTypes(const char *uri) override + { + Q_ASSERT(uri == QStringLiteral("QtWayland.Client.TextureSharing")); + qmlRegisterModule(uri, 1, 0); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + Q_UNUSED(uri); + engine->addImageProvider("wlshared", new SharedTextureProvider); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/texture-sharing/qmldir b/src/imports/texture-sharing/qmldir new file mode 100644 index 000000000..cf3b74c48 --- /dev/null +++ b/src/imports/texture-sharing/qmldir @@ -0,0 +1,3 @@ +module QtWayland.Client.TextureSharing +plugin qwaylandtexturesharing +classname QWaylandTextureSharingPlugin diff --git a/src/imports/texture-sharing/sharedtextureprovider.cpp b/src/imports/texture-sharing/sharedtextureprovider.cpp new file mode 100644 index 000000000..707e94ae6 --- /dev/null +++ b/src/imports/texture-sharing/sharedtextureprovider.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +#include "sharedtextureprovider.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "texturesharingextension.h" + +QT_BEGIN_NAMESPACE + +SharedTexture::SharedTexture(QtWaylandClient::QWaylandServerBuffer *buffer) + : m_buffer(buffer), m_tex(nullptr) +{ +} + +int SharedTexture::textureId() const +{ + updateGLTexture(); + return m_tex ? m_tex->textureId() : 0; +} + +QSize SharedTexture::textureSize() const +{ + updateGLTexture(); + return m_tex ? QSize(m_tex->width(), m_tex->height()) : QSize(); +} + +bool SharedTexture::hasAlphaChannel() const +{ + return true; +} + +bool SharedTexture::hasMipmaps() const +{ + updateGLTexture(); + return m_tex ? (m_tex->mipLevels() > 1) : false; +} + +void SharedTexture::bind() +{ + updateGLTexture(); + if (m_tex) + m_tex->bind(); +} + +inline void SharedTexture::updateGLTexture() const +{ + if (!m_tex && m_buffer) + m_tex = m_buffer->toOpenGlTexture(); +} + +class SharedTextureFactory : public QQuickTextureFactory +{ +public: + SharedTextureFactory(const QtWaylandClient::QWaylandServerBuffer *buffer, const QString &id, SharedTextureRegistry *registry) + : m_buffer(buffer), m_id(id), m_registry(registry) + { + } + + ~SharedTextureFactory() override + { + //qDebug() << "====> DESTRUCTOR SharedTextureFactory" << this; + if (m_registry) + m_registry->abandonBuffer(m_id); + delete m_buffer; // TODO: make sure we are not keeping references to this elsewhere + //qDebug() << "buffer deleted"; + } + + QSize textureSize() const override + { + return m_buffer ? m_buffer->size() : QSize(); + } + + int textureByteCount() const override + { + return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0; + } + + QSGTexture *createTexture(QQuickWindow *) const override + { + return new SharedTexture(const_cast(m_buffer)); + } + +private: + const QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr; + QString m_id; + QPointer m_registry; +}; + + +SharedTextureRegistry::SharedTextureRegistry() + : m_extension(new TextureSharingExtension) +{ + connect(m_extension, &TextureSharingExtension::bufferReceived, this, &SharedTextureRegistry::receiveBuffer); + connect(m_extension, &TextureSharingExtension::activeChanged, this, &SharedTextureRegistry::handleExtensionActive); +} + +SharedTextureRegistry::~SharedTextureRegistry() +{ + delete m_extension; +} + +const QtWaylandClient::QWaylandServerBuffer *SharedTextureRegistry::bufferForId(const QString &id) const +{ + return m_buffers.value(id); +} + +void SharedTextureRegistry::requestBuffer(const QString &id) +{ + if (!m_extension->isActive()) { + //qDebug() << "Extension not active, adding" << id << "to queue"; + m_pendingBuffers << id; + return; + } + m_extension->requestImage(id); +} + +void SharedTextureRegistry::abandonBuffer(const QString &id) +{ + m_buffers.remove(id); + m_extension->abandonImage(id); +} + + +void SharedTextureRegistry::handleExtensionActive() +{ + //qDebug() << "handleExtensionActive, queue:" << m_pendingBuffers; + if (m_extension->isActive()) + while (!m_pendingBuffers.isEmpty()) + requestBuffer(m_pendingBuffers.takeFirst()); +} + +bool SharedTextureRegistry::preinitialize() +{ + auto *serverBufferIntegration = QGuiApplicationPrivate::platformIntegration()->nativeInterface()->nativeResourceForIntegration("server_buffer_integration"); + + if (!serverBufferIntegration) { + qWarning() << "Wayland Server Buffer Integration not available."; + return false; + } + + return true; +} + +void SharedTextureRegistry::receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString& id) +{ + //qDebug() << "ReceiveBuffer for id" << id; + if (buffer) + m_buffers.insert(id, buffer); + emit replyReceived(id); +} + +class SharedTextureImageResponse : public QQuickImageResponse +{ + Q_OBJECT +public: + SharedTextureImageResponse(SharedTextureRegistry *registry, const QString &id) + : m_id(id), m_registry(registry) + { + if (!m_registry || m_registry->bufferForId(id)) { + // Shortcut: no server roundtrip needed, just let the event loop call the slot + QMetaObject::invokeMethod(this, "doResponse", Qt::QueuedConnection, Q_ARG(QString, id)); + + } else { + // TBD: timeout? + connect(registry, &SharedTextureRegistry::replyReceived, this, &SharedTextureImageResponse::doResponse); + registry->requestBuffer(id); + } + } + + QQuickTextureFactory *textureFactory() const override + { + if (m_registry) { + const QtWaylandClient::QWaylandServerBuffer *buffer = m_registry->bufferForId(m_id); + if (buffer) { + //qDebug() << "Creating shared buffer texture for" << m_id; + return new SharedTextureFactory(buffer, m_id, m_registry); + } + //qDebug() << "Shared buffer NOT found for" << m_id; + } + + // No shared buffer, do fallback + QString fbPath = fallbackPath(); + if (fbPath.isEmpty()) { + m_errorString = QStringLiteral("Shared buffer not found, and no fallback path set."); + return nullptr; + } + + QImageReader reader(fbPath + m_id); + QImage img = reader.read(); + if (img.isNull()) { + qWarning() << "Could not load local image from id/path" << reader.fileName(); + m_errorString = QStringLiteral("Shared buffer not found, and fallback local file loading failed: ") + reader.errorString(); + return nullptr; + } + return QQuickTextureFactory::textureFactoryForImage(img); + } + + QString errorString() const override + { + return m_errorString; + } + + static QString fallbackPath() + { + static QString fbPath; + static bool isInit = false; + if (!isInit) { + isInit = true; + QByteArray envVal = qgetenv("QT_SHAREDTEXTURE_FALLBACK_DIR"); + if (!envVal.isEmpty()) { + fbPath = QString::fromLocal8Bit(envVal); + if (!fbPath.endsWith(QLatin1Char('/'))) + fbPath.append(QLatin1Char('/')); + } + } + return fbPath; + } + + +public slots: + void doResponse(const QString &key) { + if (key != m_id) + return; // not our buffer + + // No need to be called again + if (m_registry) + disconnect(m_registry, &SharedTextureRegistry::replyReceived, this, &SharedTextureImageResponse::doResponse); + + emit finished(); + } + +private: + QString m_id; + SharedTextureRegistry *m_registry = nullptr; + mutable QString m_errorString; +}; + + +SharedTextureProvider::SharedTextureProvider() +{ + m_sharingAvailable = SharedTextureRegistry::preinitialize(); + if (!m_sharingAvailable) { + if (SharedTextureImageResponse::fallbackPath().isEmpty()) + qWarning() << "Shared buffer images not available, and no fallback directory set."; + else + qWarning() << "Shared buffer images not available, will fallback to local image files from" << SharedTextureImageResponse::fallbackPath(); + } +} + +SharedTextureProvider::~SharedTextureProvider() +{ + delete m_registry; +} + +QQuickImageResponse *SharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize) +{ + Q_UNUSED(requestedSize); + + //qDebug() << "Provider: got request for" << id; + + if (m_sharingAvailable && !m_registry) + m_registry = new SharedTextureRegistry; + + return new SharedTextureImageResponse(m_registry, id); +} + +QT_END_NAMESPACE + +#include "sharedtextureprovider.moc" diff --git a/src/imports/texture-sharing/sharedtextureprovider.h b/src/imports/texture-sharing/sharedtextureprovider.h new file mode 100644 index 000000000..f25c7de9c --- /dev/null +++ b/src/imports/texture-sharing/sharedtextureprovider.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** 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 SHAREDTEXTUREPROVIDER_H +#define SHAREDTEXTUREPROVIDER_H + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class TextureSharingExtension; + +class SharedTextureRegistry : public QObject +{ + Q_OBJECT +public: + SharedTextureRegistry(); + ~SharedTextureRegistry() override; + + const QtWaylandClient::QWaylandServerBuffer *bufferForId(const QString &id) const; + void requestBuffer(const QString &id); + void abandonBuffer(const QString &id); + + static bool preinitialize(); + +public slots: + void receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString &id); + +signals: + void replyReceived(const QString &id); + +private slots: + void handleExtensionActive(); + +private: + TextureSharingExtension *m_extension = nullptr; + QHash m_buffers; + QStringList m_pendingBuffers; +}; + +class SharedTextureProvider : public QQuickAsyncImageProvider +{ +public: + SharedTextureProvider(); + ~SharedTextureProvider() override; + + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; + +private: + SharedTextureRegistry *m_registry = nullptr; + bool m_sharingAvailable = false; +}; + +class SharedTexture : public QSGTexture +{ + Q_OBJECT +public: + SharedTexture(QtWaylandClient::QWaylandServerBuffer *buffer); + + int textureId() const override; + QSize textureSize() const override; + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + + void bind() override; + +private: + void updateGLTexture() const; + QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr; + mutable QOpenGLTexture *m_tex = nullptr; +}; + + +QT_END_NAMESPACE + +#endif // SHAREDTEXTUREPROVIDER_H diff --git a/src/imports/texture-sharing/texture-sharing.pro b/src/imports/texture-sharing/texture-sharing.pro new file mode 100644 index 000000000..eb5c0a9af --- /dev/null +++ b/src/imports/texture-sharing/texture-sharing.pro @@ -0,0 +1,23 @@ +CXX_MODULE = qml +TARGET = qwaylandtexturesharing +TARGETPATH = QtWayland/Client/TextureSharing +IMPORT_VERSION = 1.$$QT_MINOR_VERSION + +HEADERS += \ + sharedtextureprovider.h \ + texturesharingextension.h + +SOURCES += \ + plugin.cpp \ + sharedtextureprovider.cpp \ + texturesharingextension.cpp + +QT += quick-private qml gui-private core-private waylandclient waylandclient-private +CONFIG += wayland-scanner + +requires(qtConfig(wayland-client-texture-sharing-experimental)) + +WAYLANDCLIENTSOURCES += ../../extensions/qt-texture-sharing-unstable-v1.xml + + +load(qml_plugin) diff --git a/src/imports/texture-sharing/texturesharingextension.cpp b/src/imports/texture-sharing/texturesharingextension.cpp new file mode 100644 index 000000000..31106d694 --- /dev/null +++ b/src/imports/texture-sharing/texturesharingextension.cpp @@ -0,0 +1,86 @@ + +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "texturesharingextension.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +TextureSharingExtension::TextureSharingExtension() + : QWaylandClientExtensionTemplate(/* Supported protocol version */ 1 ) +{ + auto *wayland_integration = static_cast(QGuiApplicationPrivate::platformIntegration()); + m_server_buffer_integration = wayland_integration->serverBufferIntegration(); + if (!m_server_buffer_integration) { + qCritical() << "This application requires a working serverBufferIntegration"; + QGuiApplication::quit(); + } +} + +void TextureSharingExtension::zqt_texture_sharing_v1_provide_buffer(struct ::qt_server_buffer *buffer, const QString &key) +{ + QtWaylandClient::QWaylandServerBuffer *serverBuffer = m_server_buffer_integration->serverBuffer(buffer); + emit bufferReceived(serverBuffer, key); +} + +void TextureSharingExtension::zqt_texture_sharing_v1_image_failed(const QString &key, const QString &message) +{ + qWarning() << "TextureSharingExtension" << key << "not found" << message; + emit bufferReceived(nullptr, key); +} +void TextureSharingExtension::requestImage(const QString &key) +{ + request_image(key); +} + +void TextureSharingExtension::abandonImage(const QString &key) +{ + abandon_image(key); +} + +QT_END_NAMESPACE diff --git a/src/imports/texture-sharing/texturesharingextension.h b/src/imports/texture-sharing/texturesharingextension.h new file mode 100644 index 000000000..7b864fbc8 --- /dev/null +++ b/src/imports/texture-sharing/texturesharingextension.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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 TEXTURESHARINGEXTENSION_H +#define TEXTURESHARINGEXTENSION_H + +#include +#include +#include +#include "qwayland-qt-texture-sharing-unstable-v1.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + class QWaylandServerBuffer; + class QWaylandServerBufferIntegration; +}; + +class TextureSharingExtension : public QWaylandClientExtensionTemplate + , public QtWayland::zqt_texture_sharing_v1 +{ + Q_OBJECT +public: + TextureSharingExtension(); + +public slots: + void requestImage(const QString &key); + void abandonImage(const QString &key); + +signals: + void bufferReceived(QtWaylandClient::QWaylandServerBuffer *buffer, const QString &key); + +private: + void zqt_texture_sharing_v1_provide_buffer(struct ::qt_server_buffer *buffer, const QString &key) override; + void zqt_texture_sharing_v1_image_failed(const QString &key, const QString &message) override; + QtWaylandClient::QWaylandServerBufferIntegration *m_server_buffer_integration = nullptr; +}; + +QT_END_NAMESPACE + +#endif // TEXTURESHARINGEXTENSION_H diff --git a/src/plugins/hardwareintegration/client/client.pro b/src/plugins/hardwareintegration/client/client.pro index 82e431ee8..7b7e8a49a 100644 --- a/src/plugins/hardwareintegration/client/client.pro +++ b/src/plugins/hardwareintegration/client/client.pro @@ -18,3 +18,5 @@ qtConfig(wayland-shm-emulation-server-buffer): \ SUBDIRS += shm-emulation-server qtConfig(wayland-dmabuf-server-buffer): \ SUBDIRS += dmabuf-server +qtConfig(wayland-vulkan-server-buffer): \ + SUBDIRS += vulkan-server diff --git a/src/plugins/hardwareintegration/client/vulkan-server/main.cpp b/src/plugins/hardwareintegration/client/vulkan-server/main.cpp new file mode 100644 index 000000000..b8f64bf22 --- /dev/null +++ b/src/plugins/hardwareintegration/client/vulkan-server/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include +#include "vulkanserverbufferintegration.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class VulkanServerBufferPlugin : public QWaylandServerBufferIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandServerBufferIntegrationFactoryInterface_iid FILE "vulkan-server.json") +public: + QWaylandServerBufferIntegration *create(const QString&, const QStringList&) override; +}; + +QWaylandServerBufferIntegration *VulkanServerBufferPlugin::create(const QString& key, const QStringList& paramList) +{ + Q_UNUSED(paramList); + Q_UNUSED(key); + return new VulkanServerBufferIntegration(); +} + +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json new file mode 100644 index 000000000..baadd1529 --- /dev/null +++ b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "vulkan-server" ] +} diff --git a/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro new file mode 100644 index 000000000..1be60f7c1 --- /dev/null +++ b/src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro @@ -0,0 +1,15 @@ +# We have a bunch of C code with casts, so we can't have this option +QMAKE_CXXFLAGS_WARN_ON -= -Wcast-qual + +QT += waylandclient-private + +include(../../../../hardwareintegration/client/vulkan-server/vulkan-server.pri) + +OTHER_FILES += \ + vulkan-server.json + +SOURCES += main.cpp + +PLUGIN_TYPE = wayland-graphics-integration-client +PLUGIN_CLASS_NAME = VulkanServerBufferPlugin +load(qt_plugin) diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro index 66272e830..1912d1e9f 100644 --- a/src/plugins/hardwareintegration/compositor/compositor.pro +++ b/src/plugins/hardwareintegration/compositor/compositor.pro @@ -18,6 +18,8 @@ qtConfig(wayland-shm-emulation-server-buffer): \ SUBDIRS += shm-emulation-server qtConfig(wayland-dmabuf-server-buffer): \ SUBDIRS += dmabuf-server +qtConfig(wayland-vulkan-server-buffer): \ + SUBDIRS += vulkan-server qtConfig(wayland-egl): \ SUBDIRS += wayland-eglstream-controller diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp b/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp new file mode 100644 index 000000000..d765dd389 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include +#include "vulkanserverbufferintegration.h" + +QT_BEGIN_NAMESPACE + +class VulkanServerBufferIntegrationPlugin : public QtWayland::ServerBufferIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QtWaylandServerBufferIntegrationFactoryInterface_iid FILE "vulkan-server.json") +public: + QtWayland::ServerBufferIntegration *create(const QString&, const QStringList&) override; +}; + +QtWayland::ServerBufferIntegration *VulkanServerBufferIntegrationPlugin::create(const QString& key, const QStringList& paramList) +{ + Q_UNUSED(paramList); + Q_UNUSED(key); + return new VulkanServerBufferIntegration(); +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json new file mode 100644 index 000000000..baadd1529 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "vulkan-server" ] +} diff --git a/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro new file mode 100644 index 000000000..053654218 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro @@ -0,0 +1,12 @@ +QT = waylandcompositor waylandcompositor-private core-private gui-private + +OTHER_FILES += vulkan-server.json + +SOURCES += \ + main.cpp + +include($PWD/../../../../../hardwareintegration/compositor/vulkan-server/vulkan-server.pri) + +PLUGIN_TYPE = wayland-graphics-integration-server +PLUGIN_CLASS_NAME = VulkanServerBufferIntegrationPlugin +load(qt_plugin) diff --git a/src/src.pro b/src/src.pro index 4ecbc71b9..db7d72753 100644 --- a/src/src.pro +++ b/src/src.pro @@ -20,7 +20,7 @@ qtConfig(wayland-client) { SUBDIRS += sub_compositor sub_imports.subdir = imports - sub_imports.depends += sub-compositor + sub_imports.depends += sub-compositor sub-client sub_imports.target = sub-imports SUBDIRS += sub_imports diff --git a/sync.profile b/sync.profile index 756674cda..f8faa614e 100644 --- a/sync.profile +++ b/sync.profile @@ -58,6 +58,7 @@ "^qwayland-server-ivi-application.h", "^qwayland-server-qt-windowmanager.h", "^qwayland-server-qt-key-unstable-v1.h", + "^qwayland-server-qt-texture-sharing-unstable-v1.h", "^qwayland-server-server-buffer-extension.h", "^qwayland-server-text-input-unstable-v2.h", "^qwayland-server-touch-extension.h", @@ -69,6 +70,7 @@ "^wayland-ivi-application-server-protocol.h", "^wayland-qt-windowmanager-server-protocol.h", "^wayland-qt-key-unstable-v1-server-protocol.h", + "^wayland-qt-texture-sharing-unstable-v1-server-protocol.h", "^wayland-server-buffer-extension-server-protocol.h", "^wayland-text-input-unstable-v2-server-protocol.h", "^wayland-touch-extension-server-protocol.h", -- cgit v1.2.3