summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/vulkan_server_buffer/main.cpp60
-rw-r--r--config.tests/vulkan_server_buffer/vulkan_server_buffer.pro1
-rw-r--r--examples/wayland/wayland.pro8
-rw-r--r--src/client/configure.json17
-rw-r--r--src/client/hardwareintegration/qwaylandserverbufferintegration_p.h3
-rw-r--r--src/client/qwaylanddatadevice.cpp8
-rw-r--r--src/client/qwaylanddataoffer.cpp41
-rw-r--r--src/client/qwaylanddisplay.cpp22
-rw-r--r--src/client/qwaylanddisplay_p.h1
-rw-r--r--src/client/qwaylandinputcontext.cpp9
-rw-r--r--src/client/qwaylandinputdevice.cpp17
-rw-r--r--src/client/qwaylandintegration.cpp39
-rw-r--r--src/client/qwaylandintegration_p.h2
-rw-r--r--src/client/qwaylandqtkey.cpp6
-rw-r--r--src/client/qwaylandscreen.cpp5
-rw-r--r--src/client/qwaylandwindow.cpp6
-rw-r--r--src/client/qwaylandwindow_p.h2
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp30
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem_p.h2
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.cpp18
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.h7
-rw-r--r--src/compositor/configure.json15
-rw-r--r--src/compositor/extensions/extensions.pri5
-rw-r--r--src/compositor/extensions/qwltexturesharingextension.cpp497
-rw-r--r--src/compositor/extensions/qwltexturesharingextension_p.h159
-rw-r--r--src/compositor/hardware_integration/qwlserverbufferintegration_p.h19
-rw-r--r--src/extensions/qt-texture-sharing-unstable-v1.xml57
-rw-r--r--src/extensions/qt-vulkan-server-buffer-unstable-v1.xml64
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkan-server.pri12
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp216
-rw-r--r--src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h102
-rw-r--r--src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp35
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri16
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp325
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h111
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp733
-rw-r--r--src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h70
-rw-r--r--src/imports/imports.pro7
-rw-r--r--src/imports/texture-sharing-extension/plugin.cpp109
-rw-r--r--src/imports/texture-sharing-extension/qmldir3
-rw-r--r--src/imports/texture-sharing-extension/texture-sharing-extension.pro13
-rw-r--r--src/imports/texture-sharing/plugin.cpp97
-rw-r--r--src/imports/texture-sharing/qmldir3
-rw-r--r--src/imports/texture-sharing/sharedtextureprovider.cpp322
-rw-r--r--src/imports/texture-sharing/sharedtextureprovider.h118
-rw-r--r--src/imports/texture-sharing/texture-sharing.pro23
-rw-r--r--src/imports/texture-sharing/texturesharingextension.cpp86
-rw-r--r--src/imports/texture-sharing/texturesharingextension.h77
-rw-r--r--src/plugins/hardwareintegration/client/client.pro2
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/main.cpp66
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.json3
-rw-r--r--src/plugins/hardwareintegration/client/vulkan-server/vulkan-server.pro15
-rw-r--r--src/plugins/hardwareintegration/compositor/compositor.pro2
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/main.cpp62
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.json3
-rw-r--r--src/plugins/hardwareintegration/compositor/vulkan-server/vulkan-server.pro12
-rw-r--r--src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp2
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp2
-rw-r--r--src/qtwaylandscanner/qtwaylandscanner.cpp11
-rw-r--r--src/shared/qwaylandsharedmemoryformathelper_p.h31
-rw-r--r--src/src.pro2
-rw-r--r--sync.profile2
-rw-r--r--tests/auto/auto.pro11
-rw-r--r--tests/auto/client/client.pro1
-rw-r--r--tests/auto/client/shared/mockcompositor.h1
-rw-r--r--tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp186
-rw-r--r--tests/auto/client/xdgdecorationv1/xdgdecorationv1.pro7
67 files changed, 3899 insertions, 120 deletions
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 <vulkan/vulkan.h>
+
+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/examples/wayland/wayland.pro b/examples/wayland/wayland.pro
index b9e4263e7..5551b5703 100644
--- a/examples/wayland/wayland.pro
+++ b/examples/wayland/wayland.pro
@@ -1,7 +1,11 @@
-requires(qtHaveModule(waylandcompositor))
-requires(qtConfig(opengl))
TEMPLATE=subdirs
+!qtHaveModule(waylandcompositor): \
+ return()
+
+!qtConfig(opengl): \
+ return()
+
SUBDIRS += \
qwindow-compositor \
minimal-cpp
diff --git a/src/client/configure.json b/src/client/configure.json
index 93c5d4e49..7feeee4bf 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"
}
},
@@ -153,6 +158,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",
@@ -179,6 +194,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 632429bef..6833efd0c 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/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp
index 11984f9d3..6b2a408eb 100644
--- a/src/client/qwaylanddatadevice.cpp
+++ b/src/client/qwaylanddatadevice.cpp
@@ -154,9 +154,13 @@ void QWaylandDataDevice::data_device_drop()
void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
{
- m_enterSerial = serial;
- m_dragWindow = QWaylandWindow::fromWlSurface(surface)->window();
+ auto *dragWaylandWindow = QWaylandWindow::fromWlSurface(surface);
+ if (!dragWaylandWindow)
+ return; // Ignore foreign surfaces
+
+ m_dragWindow = dragWaylandWindow->window();
m_dragPoint = calculateDragPosition(x, y, m_dragWindow);
+ m_enterSerial = serial;
QMimeData *dragData = nullptr;
Qt::DropActions supportedActions;
diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp
index 0c732c020..3da16ed00 100644
--- a/src/client/qwaylanddataoffer.cpp
+++ b/src/client/qwaylanddataoffer.cpp
@@ -135,7 +135,7 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
}
int pipefd[2];
- if (qt_safe_pipe(pipefd, O_NONBLOCK) == -1) {
+ if (qt_safe_pipe(pipefd) == -1) {
qWarning("QWaylandMimeData: pipe2() failed");
return QVariant();
}
@@ -158,23 +158,32 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
int QWaylandMimeData::readData(int fd, QByteArray &data) const
{
- char buf[4096];
- int retryCount = 0;
- int n;
- while (true) {
- n = QT_READ(fd, buf, sizeof buf);
- if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && ++retryCount < 1000)
- usleep(1000);
- else
- break;
- }
- if (retryCount >= 1000)
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
+ if (ready < 0) {
+ qWarning() << "QWaylandDataOffer: select() failed";
+ return -1;
+ } else if (ready == 0) {
qWarning("QWaylandDataOffer: timeout reading from pipe");
- if (n > 0) {
- data.append(buf, n);
- n = readData(fd, data);
+ return -1;
+ } else {
+ char buf[4096];
+ int n = QT_READ(fd, buf, sizeof buf);
+
+ if (n > 0) {
+ data.append(buf, n);
+ n = readData(fd, data);
+ } else if (n < 0) {
+ qWarning("QWaylandDataOffer: read() failed");
+ }
+ return n;
}
- return n;
}
}
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 4a91c1c96..78524f6fc 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -186,9 +186,9 @@ void QWaylandDisplay::checkError() const
int ecode = wl_display_get_error(mDisplay);
if ((ecode == EPIPE || ecode == ECONNRESET)) {
// special case this to provide a nicer error
- qWarning("The Wayland connection broke. Did the Wayland compositor die?");
+ qFatal("The Wayland connection broke. Did the Wayland compositor die?");
} else {
- qErrnoWarning(ecode, "The Wayland connection experienced a fatal error");
+ qFatal("The Wayland connection experienced a fatal error: %s", strerror(ecode));
}
}
@@ -198,25 +198,16 @@ void QWaylandDisplay::flushRequests()
wl_display_read_events(mDisplay);
}
- if (wl_display_dispatch_pending(mDisplay) < 0) {
+ if (wl_display_dispatch_pending(mDisplay) < 0)
checkError();
- exitWithError();
- }
wl_display_flush(mDisplay);
}
void QWaylandDisplay::blockingReadEvents()
{
- if (wl_display_dispatch(mDisplay) < 0) {
+ if (wl_display_dispatch(mDisplay) < 0)
checkError();
- exitWithError();
- }
-}
-
-void QWaylandDisplay::exitWithError()
-{
- ::exit(1);
}
wl_event_queue *QWaylandDisplay::createEventQueue()
@@ -245,10 +236,9 @@ void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bo
else
wl_display_cancel_read(mDisplay);
- if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) {
+ if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0)
checkError();
- exitWithError();
- }
+
if (!condition())
break;
}
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 558d8d9b5..7cfbc19b8 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -201,7 +201,6 @@ public slots:
private:
void waitForScreens();
- void exitWithError();
void checkError() const;
void handleWaylandSync();
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index c6f287dda..0f27f551d 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -119,7 +119,8 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
return;
- struct ::wl_surface *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->object();
+ auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
+ auto *surface = window->object();
if (!surface || (surface != m_surface))
return;
@@ -157,8 +158,10 @@ void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t fla
if (queries & Qt::ImCursorRectangle) {
const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
- const QRect &tRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
- set_cursor_rectangle(tRect.x(), tRect.y(), tRect.width(), tRect.height());
+ const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
+ const QMargins margins = window->frameMargins();
+ const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top());
+ set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
}
if (queries & Qt::ImPreferredLanguage) {
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 7f6f01cd1..b5c18a074 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -562,6 +562,8 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
if (mFocus) {
qCWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
@@ -569,8 +571,9 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
<< "attempting to work around it by invalidating the current focus";
invalidateFocus();
}
+
mFocus = window;
- connect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ connect(mFocus.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
@@ -597,6 +600,10 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
if (!surface)
return;
+ auto *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
+
if (!QWaylandWindow::mouseGrab()) {
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
window->handleMouseLeave(mParent);
@@ -707,7 +714,7 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
void QWaylandInputDevice::Pointer::invalidateFocus()
{
- disconnect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ disconnect(mFocus.data(), &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
mFocus = nullptr;
mEnterSerial = 0;
}
@@ -972,9 +979,13 @@ void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
if (!surface)
return;
+ auto *window = QWaylandWindow::fromWlSurface(surface);
+ if (!window)
+ return; // Ignore foreign surfaces
+
mParent->mTime = time;
mParent->mSerial = serial;
- mFocus = QWaylandWindow::fromWlSurface(surface);
+ mFocus = window;
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed);
}
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index ea2b50b4a..85fcef43f 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -310,11 +310,14 @@ QPlatformTheme *QWaylandIntegration::createPlatformTheme(const QString &name) co
return GenericWaylandTheme::createUnixTheme(name);
}
+// May be called from non-GUI threads
QWaylandClientBufferIntegration *QWaylandIntegration::clientBufferIntegration() const
{
- if (!mClientBufferIntegrationInitialized)
+ // Do an inexpensive check first to avoid locking whenever possible
+ if (Q_UNLIKELY(!mClientBufferIntegrationInitialized))
const_cast<QWaylandIntegration *>(this)->initializeClientBufferIntegration();
+ Q_ASSERT(mClientBufferIntegrationInitialized);
return mClientBufferIntegration && mClientBufferIntegration->isValid() ? mClientBufferIntegration.data() : nullptr;
}
@@ -334,9 +337,12 @@ QWaylandShellIntegration *QWaylandIntegration::shellIntegration() const
return mShellIntegration.data();
}
+// May be called from non-GUI threads
void QWaylandIntegration::initializeClientBufferIntegration()
{
- mClientBufferIntegrationInitialized = true;
+ QMutexLocker lock(&mClientBufferInitLock);
+ if (mClientBufferIntegrationInitialized)
+ return;
QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"));
@@ -352,22 +358,25 @@ void QWaylandIntegration::initializeClientBufferIntegration()
if (targetKey.isEmpty()) {
qWarning("Failed to determine what client buffer integration to use");
- return;
- }
-
- QStringList keys = QWaylandClientBufferIntegrationFactory::keys();
- qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys;
+ } else {
+ QStringList keys = QWaylandClientBufferIntegrationFactory::keys();
+ qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys;
- if (keys.contains(targetKey))
- mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList()));
+ if (keys.contains(targetKey))
+ mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList()));
- if (mClientBufferIntegration) {
- qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey;
- mClientBufferIntegration->initialize(mDisplay.data());
- } else {
- qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey;
- qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys;
+ if (mClientBufferIntegration) {
+ qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey;
+ mClientBufferIntegration->initialize(mDisplay.data());
+ } else {
+ qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey;
+ qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys;
+ }
}
+
+ // This must be set last to make sure other threads don't use the
+ // integration before initialization is complete.
+ mClientBufferIntegrationInitialized = true;
}
void QWaylandIntegration::initializeServerBufferIntegration()
diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h
index 3aef2c4d9..a66999c7f 100644
--- a/src/client/qwaylandintegration_p.h
+++ b/src/client/qwaylandintegration_p.h
@@ -54,6 +54,7 @@
#include <QtWaylandClient/qtwaylandclientglobal.h>
#include <qpa/qplatformintegration.h>
#include <QtCore/QScopedPointer>
+#include <QtCore/QMutex>
QT_BEGIN_NAMESPACE
@@ -152,6 +153,7 @@ private:
mutable QScopedPointer<QPlatformAccessibility> mAccessibility;
#endif
bool mFailed = false;
+ QMutex mClientBufferInitLock;
bool mClientBufferIntegrationInitialized = false;
bool mServerBufferIntegrationInitialized = false;
bool mShellIntegrationInitialized = false;
diff --git a/src/client/qwaylandqtkey.cpp b/src/client/qwaylandqtkey.cpp
index a60185bd6..192619738 100644
--- a/src/client/qwaylandqtkey.cpp
+++ b/src/client/qwaylandqtkey.cpp
@@ -70,7 +70,11 @@ void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface,
}
QWaylandInputDevice *dev = inputDevices.first();
- QWaylandWindow *win = surface ? QWaylandWindow::fromWlSurface(surface) : dev->keyboardFocus();
+
+ auto *win = surface ? QWaylandWindow::fromWlSurface(surface) : nullptr;
+
+ if (!win)
+ win = dev->keyboardFocus();
if (!win || !win->window()) {
qWarning("qt_key_extension: handle_qtkey: No keyboard focus");
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index 5b04ae609..d116a807b 100644
--- a/src/client/qwaylandscreen.cpp
+++ b/src/client/qwaylandscreen.cpp
@@ -217,8 +217,9 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
{
- auto wlOutput = static_cast<QtWayland::wl_output *>(wl_output_get_user_data(output));
- return static_cast<QWaylandScreen *>(wlOutput);
+ if (auto *o = QtWayland::wl_output::fromObject(output))
+ return static_cast<QWaylandScreen *>(o);
+ return nullptr;
}
void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index f61c141c8..40289d814 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -199,7 +199,9 @@ void QWaylandWindow::initWindow()
void QWaylandWindow::initializeWlSurface()
{
+ Q_ASSERT(!isInitialized());
init(mDisplay->createSurface(static_cast<QtWayland::wl_surface *>(this)));
+ emit wlSurfaceCreated();
}
bool QWaylandWindow::shouldCreateShellSurface() const
@@ -344,7 +346,7 @@ void QWaylandWindow::setGeometry(const QRect &rect)
mSentInitialResize = true;
}
QRect exposeGeometry(QPoint(), geometry().size());
- if (exposeGeometry != mLastExposeGeometry)
+ if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry)
sendExposeEvent(exposeGeometry);
if (mShellSurface)
@@ -359,7 +361,9 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons
QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins));
mOffset += offset;
+ mInResizeFromApplyConfigure = true;
setGeometry(geometry);
+ mInResizeFromApplyConfigure = false;
}
void QWaylandWindow::sendExposeEvent(const QRect &rect)
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 861392430..9a1040288 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -203,6 +203,7 @@ public slots:
void applyConfigure();
signals:
+ void wlSurfaceCreated();
void wlSurfaceDestroyed();
protected:
@@ -269,6 +270,7 @@ private:
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreenChanged();
+ bool mInResizeFromApplyConfigure = false;
QRect mLastExposeGeometry;
static const wl_callback_listener callbackListener;
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 7b0d5c5d5..648993fc4 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -470,7 +470,7 @@ void QWaylandQuickItem::mousePressEvent(QMouseEvent *event)
return;
}
- if (!inputRegionContains(event->pos())) {
+ if (!inputRegionContains(event->localPos())) {
event->ignore();
return;
}
@@ -482,7 +482,7 @@ void QWaylandQuickItem::mousePressEvent(QMouseEvent *event)
seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->localPos()), event->windowPos());
seat->sendMousePressEvent(event->button());
- d->hoverPos = event->pos();
+ d->hoverPos = event->localPos();
}
/*!
@@ -508,7 +508,7 @@ void QWaylandQuickItem::mouseMoveEvent(QMouseEvent *event)
#endif // QT_CONFIG(draganddrop)
{
seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->localPos()), event->windowPos());
- d->hoverPos = event->pos();
+ d->hoverPos = event->localPos();
}
} else {
emit mouseMove(event->windowPos());
@@ -545,14 +545,14 @@ void QWaylandQuickItem::mouseReleaseEvent(QMouseEvent *event)
void QWaylandQuickItem::hoverEnterEvent(QHoverEvent *event)
{
Q_D(QWaylandQuickItem);
- if (!inputRegionContains(event->pos())) {
+ if (!inputRegionContains(event->posF())) {
event->ignore();
return;
}
if (d->shouldSendInputEvents()) {
QWaylandSeat *seat = compositor()->seatFor(event);
- seat->sendMouseMoveEvent(d->view.data(), event->pos(), mapToScene(event->pos()));
- d->hoverPos = event->pos();
+ seat->sendMouseMoveEvent(d->view.data(), event->posF(), mapToScene(event->posF()));
+ d->hoverPos = event->posF();
} else {
event->ignore();
}
@@ -565,16 +565,16 @@ void QWaylandQuickItem::hoverMoveEvent(QHoverEvent *event)
{
Q_D(QWaylandQuickItem);
if (surface()) {
- if (!inputRegionContains(event->pos())) {
+ if (!inputRegionContains(event->posF())) {
event->ignore();
return;
}
}
if (d->shouldSendInputEvents()) {
QWaylandSeat *seat = compositor()->seatFor(event);
- if (event->pos() != d->hoverPos) {
- seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->pos()), mapToScene(event->pos()));
- d->hoverPos = event->pos();
+ if (event->posF() != d->hoverPos) {
+ seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->posF()), mapToScene(event->posF()));
+ d->hoverPos = event->posF();
}
} else {
event->ignore();
@@ -603,7 +603,7 @@ void QWaylandQuickItem::wheelEvent(QWheelEvent *event)
{
Q_D(QWaylandQuickItem);
if (d->shouldSendInputEvents()) {
- if (!inputRegionContains(event->pos())) {
+ if (!inputRegionContains(event->posF())) {
event->ignore();
return;
}
@@ -656,10 +656,10 @@ void QWaylandQuickItem::touchEvent(QTouchEvent *event)
if (d->shouldSendInputEvents() && d->touchEventsEnabled) {
QWaylandSeat *seat = compositor()->seatFor(event);
- QPoint pointPos;
+ QPointF pointPos;
const QList<QTouchEvent::TouchPoint> &points = event->touchPoints();
if (!points.isEmpty())
- pointPos = points.at(0).pos().toPoint();
+ pointPos = points.at(0).pos();
if (event->type() == QEvent::TouchBegin && !inputRegionContains(pointPos)) {
event->ignore();
@@ -1052,7 +1052,7 @@ void QWaylandQuickItem::setFocusOnClick(bool focus)
bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) const
{
if (QWaylandSurface *s = surface())
- return s->inputRegionContains(mapToSurface(localPosition).toPoint());
+ return s->inputRegionContains(mapToSurface(localPosition));
return false;
}
@@ -1317,7 +1317,7 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
if (d->view->isBufferLocked() && !bufferHasContent && d->paintEnabled)
return oldNode;
- if (!bufferHasContent || !d->paintEnabled) {
+ if (!bufferHasContent || !d->paintEnabled || !surface()) {
delete oldNode;
return nullptr;
}
diff --git a/src/compositor/compositor_api/qwaylandquickitem_p.h b/src/compositor/compositor_api/qwaylandquickitem_p.h
index 3d710d71b..352a130dc 100644
--- a/src/compositor/compositor_api/qwaylandquickitem_p.h
+++ b/src/compositor/compositor_api/qwaylandquickitem_p.h
@@ -171,7 +171,7 @@ public:
bool focusOnClick = true;
bool sizeFollowsSurface = true;
bool belowParent = false;
- QPoint hoverPos;
+ QPointF hoverPos;
QMatrix4x4 lastMatrix;
QQuickWindow *connectedWindow = nullptr;
diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp
index c79787e6e..a82c93f7b 100644
--- a/src/compositor/compositor_api/qwaylandsurface.cpp
+++ b/src/compositor/compositor_api/qwaylandsurface.cpp
@@ -67,6 +67,7 @@
#include <QtGui/QScreen>
#include <QtCore/QDebug>
+#include <QtCore/QtMath>
QT_BEGIN_NAMESPACE
@@ -706,6 +707,23 @@ bool QWaylandSurface::inputRegionContains(const QPoint &p) const
return d->inputRegion.contains(p);
}
+//TODO: Add appropriate \since version when this is made public.
+/*!
+ * Returns \c true if the QWaylandSurface's input region contains the point \a position.
+ * Otherwise returns \c false.
+ */
+bool QWaylandSurface::inputRegionContains(const QPointF &position) const
+{
+ Q_D(const QWaylandSurface);
+ // QRegion::contains operates in integers. If a region has a rect (0,0,10,10), (0,0) is
+ // inside while (10,10) is outside. Therefore, we can't use QPoint::toPoint(), which will
+ // round upwards, meaning the point (-0.25,-0.25) would be rounded to (0,0) and count as
+ // being inside the region, and similarly, a point (9.75,9.75) inside the region would be
+ // rounded upwards and count as being outside the region.
+ const QPoint floored(qFloor(position.x()), qFloor(position.y()));
+ return d->inputRegion.contains(floored);
+}
+
/*!
* \qmlmethod void QtWaylandCompositor::WaylandSurface::destroy()
*
diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h
index 667f911c3..48b69fe21 100644
--- a/src/compositor/compositor_api/qwaylandsurface.h
+++ b/src/compositor/compositor_api/qwaylandsurface.h
@@ -130,6 +130,13 @@ public:
QWaylandCompositor *compositor() const;
bool inputRegionContains(const QPoint &p) const;
+private:
+ // TODO: Making this private now since it's added in a patch release, and we want to ensure
+ // compatibility with older patch releases.
+ // This should simply be made public (and the friend removed) in the next minor release.
+ friend class QWaylandQuickItem;
+ bool inputRegionContains(const QPointF &position) const;
+public:
Q_INVOKABLE void destroy();
Q_INVOKABLE bool isDestroyed() const;
diff --git a/src/compositor/configure.json b/src/compositor/configure.json
index aabd2472f..c26a416e0 100644
--- a/src/compositor/configure.json
+++ b/src/compositor/configure.json
@@ -86,6 +86,11 @@
"type": "compile",
"test": "dmabuf_client_buffer",
"use": "egl"
+ },
+ "vulkan-server-buffer": {
+ "label": "Vulkan Buffer Sharing",
+ "type": "compile",
+ "test": "vulkan_server_buffer"
}
},
@@ -139,6 +144,16 @@
"condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-client-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 361f93984..d8f0069de 100644
--- a/src/compositor/extensions/extensions.pri
+++ b/src/compositor/extensions/extensions.pri
@@ -83,6 +83,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 <QWaylandSurface>
+
+#include <QDebug>
+
+#include <QQuickWindow>
+
+#include <QPainter>
+#include <QPen>
+#include <QTimer>
+
+#include <QtGui/private/qtexturefilereader_p.h>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QImageReader>
+
+#include <QtQuick/QSGTexture>
+#include <QQmlContext>
+#include <QThread>
+
+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<QtWayland::ServerBuffer*>(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<QtWayland::ServerBuffer *>(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<QWaylandCompositor *>(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<QWaylandSharedTextureProvider*>(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<QWaylandCompositor *>(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 <QtCore/QMap>
+#include <QtCore/QHash>
+
+#include <QtWaylandCompositor/QWaylandCompositorExtensionTemplate>
+#include <QtWaylandCompositor/QWaylandQuickExtension>
+#include <QtWaylandCompositor/QWaylandCompositor>
+
+#include <QQuickImageProvider>
+
+#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
+#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h>
+
+#include <QtWaylandCompositor/private/qwayland-server-qt-texture-sharing-unstable-v1.h>
+
+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<SharedTextureImageResponse*> m_pendingResponses;
+};
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandTextureSharingExtension
+ : public QWaylandCompositorExtensionTemplate<QWaylandTextureSharingExtension>
+ , 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<QString, BufferInfo> 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 <QtCore/QSize>
#include <QtGui/qopengl.h>
-#include <QtWaylandCompositor/qtwaylandcompositorglobal.h>
-
+#include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h>
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 @@
+<protocol name="qt_texture_sharing_unstable_v1">
+
+ <copyright>
+ 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$
+ </copyright>
+
+ <interface name="zqt_texture_sharing_v1" version="1">
+ <request name="request_image">
+ <arg name="key" type="string"/>
+ </request>
+ <request name="abandon_image">
+ <arg name="key" type="string"/>
+ </request>
+ <event name="image_failed">
+ <arg name="key" type="string"/>
+ <arg name="error_message" type="string"/>
+ </event>
+ <event name="provide_buffer">
+ <arg name="buffer" type="object" interface="qt_server_buffer"/>
+ <arg name="key" type="string"/>
+ </event>
+ </interface>
+</protocol>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="qt_vulkan_server_buffer_unstable_v1">
+ <copyright>
+ 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$
+ </copyright>
+ <interface name="zqt_vulkan_server_buffer_v1" version="1">
+ <description summary="Internal protocol for buffer sharing using Vulkan external memory">
+ 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.
+ </description>
+ <event name="server_buffer_created">
+ <description summary="vulkan buffer information">
+ 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.
+ </description>
+ <arg name="id" type="new_id" interface="qt_server_buffer"/>
+ <arg name="fd" type="fd" summary="GL_EXT_memory_object_fd"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ <arg name="memory_size" type="uint" summary="size in bytes"/>
+ <arg name="format" type="uint" summary="GL internal format"/>
+ </event>
+ </interface>
+</protocol>
diff --git a/src/hardwareintegration/client/vulkan-server/vulkan-server.pri b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri
new file mode 100644
index 000000000..2e13a87b7
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkan-server.pri
@@ -0,0 +1,12 @@
+INCLUDEPATH += $$PWD
+
+QMAKE_USE += wayland-client
+
+SOURCES += \
+ $$PWD/vulkanserverbufferintegration.cpp
+
+HEADERS += \
+ $$PWD/vulkanserverbufferintegration.h
+
+CONFIG += wayland-scanner
+WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml
diff --git a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp
new file mode 100644
index 000000000..7e1d5966f
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "vulkanserverbufferintegration.h"
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QDebug>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QImage>
+#include <QtCore/QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+static constexpr bool extraDebug =
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ true;
+#else
+ false;
+#endif
+
+#define DECL_GL_FUNCTION(name, type) \
+ type name
+
+#define FIND_GL_FUNCTION(name, type) \
+ do { \
+ name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \
+ if (!name) { \
+ qWarning() << "ERROR in GL proc lookup. Could not find " #name; \
+ return false; \
+ } \
+ } while (0)
+
+struct VulkanServerBufferGlFunctions
+{
+ DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ bool init(QOpenGLContext *glContext)
+ {
+ FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ return true;
+ }
+ static bool create(QOpenGLContext *glContext);
+};
+
+static VulkanServerBufferGlFunctions *funcs = nullptr;
+
+bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext)
+{
+ if (funcs)
+ return true;
+ funcs = new VulkanServerBufferGlFunctions;
+ if (!funcs->init(glContext)) {
+ delete funcs;
+ funcs = nullptr;
+ return false;
+ }
+ return true;
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id,
+ int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format)
+ : m_integration(integration)
+ , m_server_buffer(id)
+ , m_fd(fd)
+ , m_memorySize(memory_size)
+ , m_internalFormat(format)
+{
+ m_size = QSize(width, height);
+}
+
+VulkanServerBuffer::~VulkanServerBuffer()
+{
+ if (QCoreApplication::closingDown())
+ return; // can't trust anything at this point
+
+ if (m_texture) { //only do gl cleanup if import has been called
+ m_integration->deleteGLTextureWhenPossible(m_texture);
+
+ if (extraDebug) qDebug() << "glDeleteMemoryObjectsEXT" << m_memoryObject;
+ funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject);
+ }
+ qt_server_buffer_release(m_server_buffer);
+ qt_server_buffer_destroy(m_server_buffer);
+}
+
+void VulkanServerBuffer::import()
+{
+ if (m_texture)
+ return;
+
+ if (extraDebug) qDebug() << "importing" << m_fd << hex << glGetError();
+
+ auto *glContext = QOpenGLContext::currentContext();
+ if (!glContext)
+ return;
+
+ if (!funcs && !VulkanServerBufferGlFunctions::create(glContext))
+ return;
+
+ funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject);
+ if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError();
+ funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, m_fd);
+ if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError();
+
+
+ m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ m_texture->create();
+
+ if (extraDebug) qDebug() << "created texture" << m_texture->textureId() << hex << glGetError();
+
+ m_texture->bind();
+ if (extraDebug) qDebug() << "bound texture" << hex << glGetError();
+ funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_internalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 );
+ if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError();
+ if (extraDebug) qDebug() << "format" << hex << m_internalFormat << GL_RGBA8;
+}
+
+QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture()
+{
+ m_integration->deleteOrphanedTextures();
+ if (!m_texture)
+ import();
+ return m_texture;
+}
+
+void VulkanServerBufferIntegration::initialize(QWaylandDisplay *display)
+{
+ m_display = display;
+ display->addRegistryListener(&wlDisplayHandleGlobal, this);
+}
+
+QWaylandServerBuffer *VulkanServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer)
+{
+ return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer));
+}
+
+void VulkanServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
+{
+ Q_UNUSED(version);
+ if (interface == "zqt_vulkan_server_buffer_v1") {
+ auto *integration = static_cast<VulkanServerBufferIntegration *>(data);
+ integration->QtWayland::zqt_vulkan_server_buffer_v1::init(registry, id, 1);
+ }
+}
+
+void VulkanServerBufferIntegration::zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format)
+{
+ if (extraDebug) qDebug() << "vulkan_server_buffer_server_buffer_created" << fd;
+ auto *server_buffer = new VulkanServerBuffer(this, id, fd, width, height, memory_size, format);
+ qt_server_buffer_set_user_data(id, server_buffer);
+}
+
+void VulkanServerBufferIntegration::deleteOrphanedTextures()
+{
+ if (!QOpenGLContext::currentContext()) {
+ qWarning("VulkanServerBufferIntegration::deleteOrphanedTextures with no current context!");
+ return;
+ }
+ qDeleteAll(orphanedTextures);
+ orphanedTextures.clear();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h
new file mode 100644
index 000000000..7add74269
--- /dev/null
+++ b/src/hardwareintegration/client/vulkan-server/vulkanserverbufferintegration.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VULKANSERVERBUFFERINTEGRATION_H
+#define VULKANSERVERBUFFERINTEGRATION_H
+
+#include <QtWaylandClient/private/qwayland-wayland.h>
+#include "qwayland-qt-vulkan-server-buffer-unstable-v1.h"
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+
+#include "vulkanserverbufferintegration.h"
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtCore/QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class VulkanServerBufferIntegration;
+
+class VulkanServerBuffer : public QWaylandServerBuffer
+{
+public:
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format);
+ ~VulkanServerBuffer() override;
+ QOpenGLTexture* toOpenGlTexture() override;
+
+private:
+ void import();
+
+ VulkanServerBufferIntegration *m_integration = nullptr;
+ struct ::qt_server_buffer *m_server_buffer = nullptr;
+ QOpenGLTexture *m_texture = nullptr;
+ int m_fd = -1;
+ uint m_memorySize = 0;
+ uint m_internalFormat = 0;
+ GLuint m_memoryObject = 0;
+};
+
+class VulkanServerBufferIntegration
+ : public QWaylandServerBufferIntegration
+ , public QtWayland::zqt_vulkan_server_buffer_v1
+{
+public:
+ void initialize(QWaylandDisplay *display) override;
+
+ QWaylandServerBuffer *serverBuffer(struct qt_server_buffer *buffer) override;
+
+ void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; }
+ void deleteOrphanedTextures();
+
+protected:
+ void zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) override;
+
+private:
+ static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id,
+ const QString &interface, uint32_t version);
+ QWaylandDisplay *m_display = nullptr;
+ QVector<QOpenGLTexture *> orphanedTextures;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
index d512613a7..cd2351e5d 100644
--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
+++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp
@@ -259,22 +259,25 @@ bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dma
LinuxDmabufClientBufferIntegration::LinuxDmabufClientBufferIntegration()
{
- m_yuvFormats.insert(DRM_FORMAT_YUYV,
- YuvFormatConversion {
- .inputPlanes = 1,
- .outputPlanes = 2,
- .plane = {{
- .format = DRM_FORMAT_GR88,
- .widthDivisor = 1,
- .heightDivisor = 1,
- .planeIndex = 0
- }, {
- .format = DRM_FORMAT_ARGB8888,
- .widthDivisor = 2,
- .heightDivisor = 1,
- .planeIndex = 0
- }}
- });
+ YuvPlaneConversion firstPlane;
+ firstPlane.format = DRM_FORMAT_GR88;
+ firstPlane.widthDivisor = 1;
+ firstPlane.heightDivisor = 1;
+ firstPlane.planeIndex = 0;
+
+ YuvPlaneConversion secondPlane;
+ secondPlane.format = DRM_FORMAT_ARGB8888;
+ secondPlane.widthDivisor = 2;
+ secondPlane.heightDivisor = 1;
+ secondPlane.planeIndex = 0;
+
+ YuvFormatConversion formatConversion;
+ formatConversion.inputPlanes = 1;
+ formatConversion.outputPlanes = 2;
+ formatConversion.plane[0] = firstPlane;
+ formatConversion.plane[1] = secondPlane;
+
+ m_yuvFormats.insert(DRM_FORMAT_YUYV, formatConversion);
}
LinuxDmabufClientBufferIntegration::~LinuxDmabufClientBufferIntegration()
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri
new file mode 100644
index 000000000..63a96ad0f
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkan-server.pri
@@ -0,0 +1,16 @@
+INCLUDEPATH += $$PWD $$PWD/../../../3rdparty/util
+
+QT += vulkan_support-private
+
+QMAKE_USE_PRIVATE += wayland-server
+
+SOURCES += \
+ $$PWD/vulkanserverbufferintegration.cpp \
+ $$PWD/vulkanwrapper.cpp
+
+HEADERS += \
+ $$PWD/vulkanserverbufferintegration.h \
+ $$PWD/vulkanwrapper.h
+
+CONFIG += wayland-scanner
+WAYLANDSERVERSOURCES += $$PWD/../../../extensions/qt-vulkan-server-buffer-unstable-v1.xml
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp
new file mode 100644
index 000000000..7f9f8a151
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "vulkanserverbufferintegration.h"
+
+#include "vulkanwrapper.h"
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLTexture>
+#include <QtGui/QOffscreenSurface>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+static constexpr bool extraDebug = false;
+
+#define DECL_GL_FUNCTION(name, type) \
+ type name
+
+#define FIND_GL_FUNCTION(name, type) \
+ do { \
+ name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \
+ if (!name) { \
+ qWarning() << "ERROR in GL proc lookup. Could not find " #name; \
+ return false; \
+ } \
+ } while (0)
+
+struct VulkanServerBufferGlFunctions
+{
+ DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ //DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ bool init(QOpenGLContext *glContext)
+ {
+ FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
+ FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
+ //FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
+ FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
+
+ return true;
+ }
+ static bool create(QOpenGLContext *glContext);
+};
+
+static VulkanServerBufferGlFunctions *funcs = nullptr;
+
+//RAII
+class CurrentContext
+{
+public:
+ CurrentContext()
+ {
+ if (!QOpenGLContext::currentContext()) {
+ if (QOpenGLContext::globalShareContext()) {
+ if (!localContext) {
+ localContext = new QOpenGLContext;
+ localContext->setShareContext(QOpenGLContext::globalShareContext());
+ localContext->create();
+ }
+ if (!offscreenSurface) {
+ offscreenSurface = new QOffscreenSurface;
+ offscreenSurface->setFormat(localContext->format());
+ offscreenSurface->create();
+ }
+ localContext->makeCurrent(offscreenSurface);
+ localContextInUse = true;
+ } else {
+ qCritical("VulkanServerBufferIntegration: no globalShareContext");
+ }
+ }
+ }
+ ~CurrentContext()
+ {
+ if (localContextInUse)
+ localContext->doneCurrent();
+ }
+ QOpenGLContext *context() { return localContextInUse ? localContext : QOpenGLContext::currentContext(); }
+private:
+ static QOpenGLContext *localContext;
+ static QOffscreenSurface *offscreenSurface;
+ bool localContextInUse = false;
+};
+
+QOpenGLContext *CurrentContext::localContext = nullptr;
+QOffscreenSurface *CurrentContext::offscreenSurface = nullptr;
+
+bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext)
+{
+ if (funcs)
+ return true;
+ funcs = new VulkanServerBufferGlFunctions;
+ if (!funcs->init(glContext)) {
+ delete funcs;
+ funcs = nullptr;
+ return false;
+ }
+ return true;
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format)
+ : QtWayland::ServerBuffer(qimage.size(),format)
+ , m_integration(integration)
+ , m_width(qimage.width())
+ , m_height(qimage.height())
+{
+ m_format = format;
+ switch (m_format) {
+ case RGBA32:
+ m_glInternalFormat = GL_RGBA8;
+ break;
+ // case A8:
+ // m_glInternalFormat = GL_R8;
+ // break;
+ default:
+ qWarning("VulkanServerBuffer: unsupported format");
+ m_glInternalFormat = GL_RGBA8;
+ break;
+ }
+
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ m_vImage = vulkanWrapper->createTextureImage(qimage);
+ if (m_vImage)
+ m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize);
+}
+
+VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size)
+ : QtWayland::ServerBuffer(size, QtWayland::ServerBuffer::Custom)
+ , m_integration(integration)
+ , m_width(size.width())
+ , m_height(size.height())
+ , m_vImage(vImage)
+ , m_glInternalFormat(glInternalFormat)
+{
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ m_fd = vulkanWrapper->getImageInfo(m_vImage, &m_memorySize);
+}
+
+VulkanServerBuffer::~VulkanServerBuffer()
+{
+ delete m_texture; //this is always nullptr for now
+ auto vulkanWrapper = m_integration->vulkanWrapper();
+ vulkanWrapper->freeTextureImage(m_vImage);
+}
+
+struct ::wl_resource *VulkanServerBuffer::resourceForClient(struct ::wl_client *client)
+{
+ auto *bufferResource = resourceMap().value(client);
+ if (!bufferResource) {
+ auto integrationResource = m_integration->resourceMap().value(client);
+ if (!integrationResource) {
+ qWarning("VulkanServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the vulkan interface");
+ return nullptr;
+ }
+ struct ::wl_resource *shm_integration_resource = integrationResource->handle;
+ Resource *resource = add(client, 1);
+ m_integration->send_server_buffer_created(shm_integration_resource, resource->handle, m_fd, m_width, m_height, m_memorySize, m_glInternalFormat);
+ return resource->handle;
+ }
+ return bufferResource->handle;
+}
+
+QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture()
+{
+ if (m_texture && m_texture->isCreated())
+ return m_texture;
+
+ CurrentContext current;
+
+ if (!funcs && !VulkanServerBufferGlFunctions::create(current.context()))
+ return nullptr;
+
+ funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject);
+ if (extraDebug) qDebug() << "glCreateMemoryObjectsEXT" << hex << glGetError();
+
+
+ int dupfd = fcntl(m_fd, F_DUPFD_CLOEXEC, 0);
+ if (dupfd < 0) {
+ perror("VulkanServerBuffer::toOpenGlTexture() Could not dup fd:");
+ return nullptr;
+ }
+
+ funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, dupfd);
+ if (extraDebug) qDebug() << "glImportMemoryFdEXT" << hex << glGetError();
+
+
+ if (!m_texture)
+ m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ m_texture->create();
+
+ GLuint texId = m_texture->textureId();
+ if (extraDebug) qDebug() << "created texture" << texId << hex << glGetError();
+
+ m_texture->bind();
+ if (extraDebug) qDebug() << "bound texture" << texId << hex << glGetError();
+ funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_glInternalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 );
+ if (extraDebug) qDebug() << "glTexStorageMem2DEXT" << hex << glGetError();
+ if (extraDebug) qDebug() << "format" << hex << m_glInternalFormat << GL_RGBA8;
+
+
+ return m_texture;
+}
+
+void VulkanServerBuffer::releaseOpenGlTexture()
+{
+ if (!m_texture || !m_texture->isCreated())
+ return;
+
+ CurrentContext current;
+ m_texture->destroy();
+ funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject);
+}
+
+
+bool VulkanServerBuffer::bufferInUse()
+{
+ return (m_texture && m_texture->isCreated()) || resourceMap().count() > 0;
+}
+
+void VulkanServerBuffer::server_buffer_release(Resource *resource)
+{
+ qCDebug(qLcWaylandCompositorHardwareIntegration) << "server_buffer RELEASE resource" << resource->handle << wl_resource_get_id(resource->handle) << "for client" << resource->client();
+ wl_resource_destroy(resource->handle);
+}
+
+VulkanServerBufferIntegration::VulkanServerBufferIntegration()
+{
+}
+
+VulkanServerBufferIntegration::~VulkanServerBufferIntegration()
+{
+}
+
+void VulkanServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor)
+{
+ Q_ASSERT(QGuiApplication::platformNativeInterface());
+
+ QtWaylandServer::zqt_vulkan_server_buffer_v1::init(compositor->display(), 1);
+}
+
+bool VulkanServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const
+{
+ switch (format) {
+ case QtWayland::ServerBuffer::RGBA32:
+ return true;
+ case QtWayland::ServerBuffer::A8:
+ return false;
+ default:
+ return false;
+ }
+}
+
+QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format)
+{
+ if (!m_vulkanWrapper) {
+ CurrentContext current;
+ m_vulkanWrapper = new VulkanWrapper(current.context());
+ }
+ return new VulkanServerBuffer(this, qimage, format);
+}
+
+QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat)
+{
+ if (!m_vulkanWrapper) {
+ CurrentContext current;
+ m_vulkanWrapper = new VulkanWrapper(current.context());
+ }
+
+ auto *vImage = m_vulkanWrapper->createTextureImageFromData(reinterpret_cast<const uchar*>(data.constData()), data.size(), size, glInternalFormat);
+
+ if (vImage)
+ return new VulkanServerBuffer(this, vImage, glInternalFormat, size);
+
+ qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not load compressed texture";
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h
new file mode 100644
index 000000000..7246e36df
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VULKANSERVERBUFFERINTEGRATION_H
+#define VULKANSERVERBUFFERINTEGRATION_H
+
+#include <QtWaylandCompositor/private/qwlserverbufferintegration_p.h>
+
+#include "qwayland-server-qt-vulkan-server-buffer-unstable-v1.h"
+
+#include <QtGui/QImage>
+#include <QtGui/QWindow>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <QtGui/QGuiApplication>
+
+#include <QtWaylandCompositor/qwaylandcompositor.h>
+#include <QtWaylandCompositor/private/qwayland-server-server-buffer-extension.h>
+
+QT_BEGIN_NAMESPACE
+
+class VulkanServerBufferIntegration;
+class VulkanWrapper;
+struct VulkanImageWrapper;
+
+class VulkanServerBuffer : public QtWayland::ServerBuffer, public QtWaylandServer::qt_server_buffer
+{
+public:
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format);
+ VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size);
+ ~VulkanServerBuffer() override;
+
+ struct ::wl_resource *resourceForClient(struct ::wl_client *) override;
+ bool bufferInUse() override;
+ QOpenGLTexture *toOpenGlTexture() override;
+ void releaseOpenGlTexture() override;
+
+protected:
+ void server_buffer_release(Resource *resource) override;
+
+private:
+ VulkanServerBufferIntegration *m_integration = nullptr;
+
+ int m_width;
+ int m_height;
+ int m_memorySize;
+ int m_fd = -1;
+ VulkanImageWrapper *m_vImage = nullptr;
+ QOpenGLTexture *m_texture = nullptr;
+ uint m_glInternalFormat;
+ GLuint m_memoryObject;
+};
+
+class VulkanServerBufferIntegration :
+ public QtWayland::ServerBufferIntegration,
+ public QtWaylandServer::zqt_vulkan_server_buffer_v1
+{
+public:
+ VulkanServerBufferIntegration();
+ ~VulkanServerBufferIntegration() override;
+
+ VulkanWrapper *vulkanWrapper() const { return m_vulkanWrapper; }
+
+ void initializeHardware(QWaylandCompositor *) override;
+
+ bool supportsFormat(QtWayland::ServerBuffer::Format format) const override;
+ QtWayland::ServerBuffer *createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format) override;
+ QtWayland::ServerBuffer *createServerBufferFromData(const QByteArray &data, const QSize &size, uint glInternalFormat) override;
+
+private:
+ VulkanWrapper *m_vulkanWrapper = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp
new file mode 100644
index 000000000..fe66adf55
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp
@@ -0,0 +1,733 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// NOTE: Some of the code below is adapted from the public domain code at https://vulkan-tutorial.com/
+
+#define GL_GLEXT_PROTOTYPES
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <QtVulkanSupport/private/qvkconvenience_p.h>
+
+#include "vulkanwrapper.h"
+
+#include <QImage>
+#include <QOpenGLContext>
+
+#include <set>
+
+#include <unistd.h>
+
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static constexpr bool extraDebug = false;
+
+#define DECL_VK_FUNCTION(name) \
+ PFN_ ## name name = nullptr;
+
+#define IMPL_VK_FUNCTION(name) \
+ name = reinterpret_cast<PFN_ ## name>(f_glGetVkProcAddrNV(#name)); \
+ if (!name) { \
+ qCritical() << "ERROR in Vulkan proc lookup. Could not find " #name; \
+ }
+
+struct QueueFamilyIndices {
+ int graphicsFamily = -1;
+ int presentFamily = -1;
+
+ bool isComplete() {
+ return graphicsFamily >= 0 && presentFamily >= 0;
+ }
+};
+
+class VulkanWrapperPrivate
+{
+public:
+ explicit VulkanWrapperPrivate(QOpenGLContext *glContext);
+
+ VulkanImageWrapper *createTextureImage(const QImage &img);
+ VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat);
+
+ void freeTextureImage(VulkanImageWrapper *imageWrapper);
+
+private:
+ DECL_VK_FUNCTION(vkAllocateCommandBuffers);
+ DECL_VK_FUNCTION(vkAllocateMemory);
+ DECL_VK_FUNCTION(vkBeginCommandBuffer);
+ DECL_VK_FUNCTION(vkBindImageMemory);
+ DECL_VK_FUNCTION(vkCmdCopyBufferToImage);
+ DECL_VK_FUNCTION(vkCmdPipelineBarrier);
+ DECL_VK_FUNCTION(vkCreateImage);
+ DECL_VK_FUNCTION(vkDestroyImage);
+ DECL_VK_FUNCTION(vkDestroyBuffer);
+ DECL_VK_FUNCTION(vkEndCommandBuffer);
+ DECL_VK_FUNCTION(vkFreeCommandBuffers);
+ DECL_VK_FUNCTION(vkFreeMemory);
+ DECL_VK_FUNCTION(vkGetImageMemoryRequirements);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
+ DECL_VK_FUNCTION(vkMapMemory);
+ DECL_VK_FUNCTION(vkQueueSubmit);
+ DECL_VK_FUNCTION(vkQueueWaitIdle);
+ DECL_VK_FUNCTION(vkUnmapMemory);
+ DECL_VK_FUNCTION(vkCreateBuffer);
+ DECL_VK_FUNCTION(vkGetBufferMemoryRequirements);
+ DECL_VK_FUNCTION(vkBindBufferMemory);
+
+ DECL_VK_FUNCTION(vkCreateInstance);
+ DECL_VK_FUNCTION(vkEnumeratePhysicalDevices);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceProperties);
+ DECL_VK_FUNCTION(vkCreateDevice);
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties);
+
+ DECL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
+ DECL_VK_FUNCTION(vkCreateCommandPool);
+
+ DECL_VK_FUNCTION(vkGetDeviceQueue);
+ DECL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR);
+ DECL_VK_FUNCTION(vkGetMemoryFdKHR);
+
+ //DECL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
+
+ void initFunctions(PFNGLGETVKPROCADDRNVPROC f_glGetVkProcAddrNV) {
+ IMPL_VK_FUNCTION(vkAllocateCommandBuffers);
+ IMPL_VK_FUNCTION(vkAllocateMemory);
+ IMPL_VK_FUNCTION(vkBeginCommandBuffer);
+ IMPL_VK_FUNCTION(vkBindImageMemory);
+ IMPL_VK_FUNCTION(vkCmdCopyBufferToImage);
+ IMPL_VK_FUNCTION(vkCmdPipelineBarrier);
+ IMPL_VK_FUNCTION(vkCreateImage);
+ IMPL_VK_FUNCTION(vkDestroyImage);
+ IMPL_VK_FUNCTION(vkDestroyBuffer);
+ IMPL_VK_FUNCTION(vkEndCommandBuffer);
+ IMPL_VK_FUNCTION(vkFreeCommandBuffers);
+ IMPL_VK_FUNCTION(vkFreeMemory);
+ IMPL_VK_FUNCTION(vkGetImageMemoryRequirements);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
+ IMPL_VK_FUNCTION(vkMapMemory);
+ IMPL_VK_FUNCTION(vkQueueSubmit);
+ IMPL_VK_FUNCTION(vkQueueWaitIdle);
+ IMPL_VK_FUNCTION(vkUnmapMemory);
+ IMPL_VK_FUNCTION(vkCreateBuffer);
+ IMPL_VK_FUNCTION(vkGetBufferMemoryRequirements);
+ IMPL_VK_FUNCTION(vkBindBufferMemory);
+
+ IMPL_VK_FUNCTION(vkCreateInstance);
+ IMPL_VK_FUNCTION(vkEnumeratePhysicalDevices);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceProperties);
+ IMPL_VK_FUNCTION(vkCreateDevice);
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties);
+
+ IMPL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
+ IMPL_VK_FUNCTION(vkCreateCommandPool);
+
+ IMPL_VK_FUNCTION(vkGetDeviceQueue);
+ IMPL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR);
+ IMPL_VK_FUNCTION(vkGetMemoryFdKHR);
+
+ //IMPL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
+ }
+
+ int findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
+
+ VulkanImageWrapper *createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize);
+ bool transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout);
+ bool createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
+ VkCommandBuffer beginSingleTimeCommands();
+ void endSingleTimeCommands(VkCommandBuffer commandBuffer);
+ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
+ void createCommandPool();
+ QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
+ bool createLogicalDevice();
+
+private:
+ VkInstance m_instance = VK_NULL_HANDLE;
+ VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
+ VkDevice m_device = VK_NULL_HANDLE;
+ VkCommandPool m_commandPool = VK_NULL_HANDLE;
+
+ VkQueue m_graphicsQueue = VK_NULL_HANDLE;
+
+ bool m_initFailed = false;
+};
+
+struct VulkanImageWrapper
+{
+ VkImage textureImage = VK_NULL_HANDLE;
+ int imgMemSize = -1;
+ QSize imgSize;
+ int imgFd = -1;
+ VkDeviceMemory textureImageMemory = VK_NULL_HANDLE;
+};
+
+int VulkanWrapperPrivate::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
+{
+ VkPhysicalDeviceMemoryProperties memProperties;
+ vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);
+
+ for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
+ if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
+ return i;
+ }
+ }
+
+ qCritical("VulkanWrapper: failed to find suitable memory type!");
+ return -1;
+}
+
+
+VulkanImageWrapper *VulkanWrapperPrivate::createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize)
+{
+ VkImageCreateInfo imageInfo = {};
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent.width = size.width();
+ imageInfo.extent.height = size.height();
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+ imageInfo.format = format;
+ imageInfo.tiling = tiling;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageInfo.usage = usage;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ VkImage image = VK_NULL_HANDLE;
+
+ if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create image!");
+ return nullptr;
+ }
+
+ QScopedPointer<VulkanImageWrapper> imageWrapper(new VulkanImageWrapper);
+ imageWrapper->textureImage = image;
+ imageWrapper->imgMemSize = memSize;
+ imageWrapper->imgSize = size;
+
+ VkMemoryRequirements memRequirements;
+ vkGetImageMemoryRequirements(m_device, image, &memRequirements);
+
+ VkExportMemoryAllocateInfoKHR exportAllocInfo = {};
+ exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
+ exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ int memoryType = findMemoryType(memRequirements.memoryTypeBits, properties);
+ if (memoryType < 0)
+ return nullptr;
+ allocInfo.memoryTypeIndex = memoryType;
+ allocInfo.pNext = &exportAllocInfo;
+
+ if (vkAllocateMemory(m_device, &allocInfo, nullptr, &imageWrapper->textureImageMemory) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to allocate image memory!");
+ return nullptr;
+ }
+
+ int res = vkBindImageMemory(m_device, image, imageWrapper->textureImageMemory, 0);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "vkBindImageMemory res" << res;
+
+ VkMemoryGetFdInfoKHR memoryFdInfo = {};
+ memoryFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
+ memoryFdInfo.memory = imageWrapper->textureImageMemory;
+ memoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+ res = vkGetMemoryFdKHR(m_device, &memoryFdInfo, &imageWrapper->imgFd);
+ if (extraDebug) qDebug() << "vkGetMemoryFdKHR res" << res << "fd" << imageWrapper->imgFd;
+
+ return imageWrapper.take();
+}
+
+
+bool VulkanWrapperPrivate::transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout)
+{
+ VkCommandBuffer commandBuffer = beginSingleTimeCommands();
+
+ VkImageMemoryBarrier barrier = {};
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = image;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = 1;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = 1;
+
+ VkPipelineStageFlags sourceStage;
+ VkPipelineStageFlags destinationStage;
+
+ if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
+ barrier.srcAccessMask = 0;
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+
+ sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ } else {
+ qCritical("VulkanWrapper: unsupported layout transition!");
+ return false;
+ }
+
+ vkCmdPipelineBarrier(
+ commandBuffer,
+ sourceStage, destinationStage,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &barrier
+ );
+
+ endSingleTimeCommands(commandBuffer);
+ return true;
+}
+
+bool VulkanWrapperPrivate::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
+{
+ VkBufferCreateInfo bufferInfo = {};
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = size;
+ bufferInfo.usage = usage;
+ bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create buffer!");
+ return false;
+ }
+
+ VkMemoryRequirements memRequirements;
+ vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements);
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
+
+ if (vkAllocateMemory(m_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to allocate buffer memory!");
+ return false;
+ }
+
+ vkBindBufferMemory(m_device, buffer, bufferMemory, 0);
+ return true;
+}
+
+
+VkCommandBuffer VulkanWrapperPrivate::beginSingleTimeCommands()
+{
+ VkCommandBufferAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ allocInfo.commandPool = m_commandPool;
+ allocInfo.commandBufferCount = 1;
+
+ if (extraDebug) qDebug() << "allocating...";
+
+ VkCommandBuffer commandBuffer;
+ int res = vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "vkAllocateCommandBuffers res" << res;
+
+ VkCommandBufferBeginInfo beginInfo = {};
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+ res = vkBeginCommandBuffer(commandBuffer, &beginInfo);
+ if (extraDebug) qDebug() << "BEGIN res" << res;
+
+ return commandBuffer;
+}
+
+void VulkanWrapperPrivate::endSingleTimeCommands(VkCommandBuffer commandBuffer)
+{
+ int res = vkEndCommandBuffer(commandBuffer);
+ Q_UNUSED(res);
+ if (extraDebug) qDebug() << "END res" << res;
+
+ VkSubmitInfo submitInfo = {};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &commandBuffer;
+
+ vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+ vkQueueWaitIdle(m_graphicsQueue);
+
+ vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
+}
+
+void VulkanWrapperPrivate::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
+{
+ VkCommandBuffer commandBuffer = beginSingleTimeCommands();
+
+ VkBufferImageCopy region = {};
+ region.bufferOffset = 0;
+ region.bufferRowLength = 0;
+ region.bufferImageHeight = 0;
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = 0;
+ region.imageSubresource.layerCount = 1;
+ region.imageOffset = {0, 0, 0};
+ region.imageExtent = {
+ width,
+ height,
+ 1
+ };
+
+ vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
+
+ endSingleTimeCommands(commandBuffer);
+}
+
+void VulkanWrapperPrivate::createCommandPool()
+{
+ QueueFamilyIndices queueFamilyIndices = findQueueFamilies(m_physicalDevice);
+
+ VkCommandPoolCreateInfo poolInfo = {};
+ poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
+
+ if (vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) {
+ m_initFailed = true;
+ qCritical("VulkanWrapperPrivate: could not create command pool");
+ }
+}
+
+QueueFamilyIndices VulkanWrapperPrivate::findQueueFamilies(VkPhysicalDevice device)
+{
+ QueueFamilyIndices indices;
+
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
+ if (extraDebug) qDebug() << "queueFamilyCount" << queueFamilyCount;
+
+
+ std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ for (const auto& queueFamily : queueFamilies) {
+ qDebug() << "....q" << "count" << queueFamily.queueCount << queueFamily.timestampValidBits << hex << queueFamily.queueFlags;
+ }
+#endif
+
+ int i = 0;
+ for (const auto& queueFamily : queueFamilies) {
+ if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ indices.graphicsFamily = i;
+ break;
+ }
+ i++;
+ }
+
+ return indices;
+}
+
+bool VulkanWrapperPrivate::createLogicalDevice()
+{
+ QueueFamilyIndices indices = findQueueFamilies(m_physicalDevice);
+
+ std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
+ std::set<int> uniqueQueueFamilies = {indices.graphicsFamily}; //////, indices.presentFamily};
+
+ float queuePriority = 1.0f;
+ for (int queueFamily : uniqueQueueFamilies) {
+ VkDeviceQueueCreateInfo queueCreateInfo = {};
+ queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueCreateInfo.queueFamilyIndex = queueFamily;
+ queueCreateInfo.queueCount = 1;
+ queueCreateInfo.pQueuePriorities = &queuePriority;
+ queueCreateInfos.push_back(queueCreateInfo);
+ }
+
+ VkPhysicalDeviceFeatures deviceFeatures = {};
+
+ VkDeviceCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+
+ createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
+ createInfo.pQueueCreateInfos = queueCreateInfos.data();
+
+ createInfo.pEnabledFeatures = &deviceFeatures;
+
+ if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) {
+ qCritical("VulkanWrapper: failed to create logical device!");
+ return false;
+ }
+
+ vkGetDeviceQueue(m_device, indices.graphicsFamily, 0, &m_graphicsQueue);
+ return true;
+}
+
+VulkanImageWrapper *VulkanWrapperPrivate::createTextureImage(const QImage &img)
+{
+ return createTextureImageFromData(img.constBits(), img.sizeInBytes(), img.size(), VK_FORMAT_R8G8B8A8_UNORM);
+}
+
+VulkanImageWrapper *VulkanWrapperPrivate::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat)
+{
+ if (m_initFailed)
+ return nullptr;
+
+ int texWidth = size.width();
+ int texHeight = size.height();
+ bool ok;
+ if (extraDebug) qDebug("image load %p %dx%d", pixels, texWidth, texHeight);
+ if (!pixels) {
+ qCritical("VulkanWrapper: failed to load texture image!");
+ return nullptr;
+ }
+
+ VkBuffer stagingBuffer;
+ VkDeviceMemory stagingBufferMemory;
+ ok = createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
+
+ if (!ok)
+ return nullptr;
+
+ void* data;
+ vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data);
+ if (extraDebug) qDebug() << "mapped" << data << bufferSize;
+ memcpy(data, pixels, static_cast<size_t>(bufferSize));
+ vkUnmapMemory(m_device, stagingBufferMemory);
+
+ if (extraDebug) qDebug() << "creating image...";
+
+ QScopedPointer<VulkanImageWrapper> imageWrapper(createImage(vkFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, bufferSize));
+ if (imageWrapper.isNull())
+ return nullptr;
+
+ if (extraDebug) qDebug() << "transition...";
+
+ const VkImage textureImage = imageWrapper->textureImage;
+
+ ok = transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ if (!ok)
+ return nullptr;
+
+ if (extraDebug) qDebug() << "copyBufferToImage...";
+ copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
+ transitionImageLayout(textureImage, vkFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vkDestroyBuffer(m_device, stagingBuffer, nullptr);
+ vkFreeMemory(m_device, stagingBufferMemory, nullptr);
+
+ return imageWrapper.take();
+}
+
+void VulkanWrapperPrivate::freeTextureImage(VulkanImageWrapper *imageWrapper)
+{
+ if (!imageWrapper)
+ return;
+
+ //"To avoid leaking resources, the application must release ownership of the file descriptor using the close system call"
+ ::close(imageWrapper->imgFd);
+
+ // clean up the image memory
+ vkDestroyImage(m_device, imageWrapper->textureImage, nullptr);
+ vkFreeMemory(m_device, imageWrapper->textureImageMemory, nullptr);
+}
+
+VulkanWrapperPrivate::VulkanWrapperPrivate(QOpenGLContext *glContext)
+{
+ if (extraDebug) qDebug("Creating Vulkan instance");
+ VkApplicationInfo applicationInfo = {};
+ applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ applicationInfo.pNext = nullptr;
+ applicationInfo.pApplicationName = nullptr;
+ applicationInfo.applicationVersion = 0;
+ applicationInfo.pEngineName = nullptr;
+ applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+ applicationInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5);
+
+ VkInstanceCreateInfo instanceCreateInfo = {};
+ instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instanceCreateInfo.pNext = nullptr;
+ instanceCreateInfo.flags = 0;
+ instanceCreateInfo.pApplicationInfo = &applicationInfo;
+ instanceCreateInfo.enabledLayerCount = 0;
+ instanceCreateInfo.ppEnabledLayerNames = nullptr;
+ instanceCreateInfo.enabledExtensionCount = 0;
+ instanceCreateInfo.ppEnabledExtensionNames = nullptr;
+
+ auto f_glGetVkProcAddrNV = reinterpret_cast<PFNGLGETVKPROCADDRNVPROC>(glContext->getProcAddress("glGetVkProcAddrNV"));
+
+ if (!f_glGetVkProcAddrNV) {
+ qCritical("VulkanWrapper: Could not find Vulkan/GL interop function glGetVkProcAddrNV");
+ m_initFailed = true;
+ return;
+ }
+
+ initFunctions(f_glGetVkProcAddrNV);
+
+ VkResult instanceCreationResult = vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance);
+
+ if (extraDebug) qDebug() << "result" << instanceCreationResult;
+
+ if (instanceCreationResult != VK_SUCCESS) {
+ qCritical() << "VulkanWrapper: Failed to create Vulkan instance: Error "
+ << instanceCreationResult;
+ m_initFailed = true;
+ return;
+ }
+
+ uint32_t devCount;
+
+ auto res = vkEnumeratePhysicalDevices(m_instance, &devCount, nullptr);
+ if (extraDebug) qDebug() << "vkEnumeratePhysicalDevices res =" << res << "count =" << devCount;
+
+ QVarLengthArray<VkPhysicalDevice, 5> dev(devCount);
+
+ res = vkEnumeratePhysicalDevices(m_instance, &devCount, dev.data());
+ if (extraDebug) qDebug() << "...devs res =" << res << "count =" << devCount;
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ VkPhysicalDeviceProperties props;
+
+ vkGetPhysicalDeviceProperties(dev[0], &props);
+
+ qDebug() << "Properties " << hex
+ << "apiVersion" << props.apiVersion
+ << "driverVersion" << props.driverVersion
+ << "vendorID" << props.vendorID
+ << "deviceID" << props.deviceID
+ << "deviceType" << props.deviceType
+ << "deviceName" << props.deviceName;
+#endif
+
+ m_physicalDevice = dev[0]; //TODO handle the case of multiple GPUs where only some support Vulkan
+
+ bool ok = createLogicalDevice();
+ if (!ok) {
+ qCritical("VulkanWrapperPrivate: could not create logical device");
+ m_initFailed = true;
+ return;
+ }
+
+ VkPhysicalDeviceMemoryProperties memProps;
+
+
+ vkGetPhysicalDeviceMemoryProperties(dev[0], &memProps);
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ qDebug() << "Physical memory properties:\n" << "types:" << memProps.memoryTypeCount << "heaps:" << memProps.memoryHeapCount;
+ for (uint i = 0; i < memProps.memoryTypeCount; ++i)
+ qDebug() << " " << i << "heap" << memProps.memoryTypes[i].heapIndex << "flags" << hex << memProps.memoryTypes[i].propertyFlags;
+
+ for (uint i = 0; i < memProps.memoryHeapCount; ++i)
+ qDebug() << " " << i << "size" << memProps.memoryHeaps[i].size << "flags" << hex << memProps.memoryHeaps[i].flags;
+#endif
+
+ int gpuMemoryType = -1;
+
+ for (uint i = 0; i < memProps.memoryTypeCount; ++i) {
+ if (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ gpuMemoryType = i;
+ break;
+ }
+ }
+
+ if (gpuMemoryType < 0) {
+ qCritical("VulkanWrapper: Could not find GPU memory!");
+ m_initFailed = true;
+ return;
+ }
+
+#ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG
+ qDebug() << "GPU memory type:" << gpuMemoryType << "heap:" << memProps.memoryTypes[gpuMemoryType].heapIndex;
+
+ for (int f = 0; f <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; f++)
+ {
+ VkFormatProperties formatProps;
+ vkGetPhysicalDeviceFormatProperties(dev[0], VkFormat(f), &formatProps);
+ qDebug() << "format" << f << "features" << hex << formatProps.linearTilingFeatures << formatProps.optimalTilingFeatures << formatProps.bufferFeatures;
+ }
+#endif
+ createCommandPool();
+}
+
+
+VulkanWrapper::VulkanWrapper(QOpenGLContext *glContext)
+ : d_ptr(new VulkanWrapperPrivate(glContext))
+{
+}
+
+VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &img)
+{
+ return d_ptr->createTextureImage(img);
+}
+
+VulkanImageWrapper *VulkanWrapper::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat)
+{
+ VkFormat vkFormat = VkFormat(QVkConvenience::vkFormatFromGlFormat(glInternalFormat));
+
+ if (vkFormat == VK_FORMAT_UNDEFINED)
+ return nullptr;
+
+ return d_ptr->createTextureImageFromData(pixels, bufferSize, size, vkFormat);
+}
+
+int VulkanWrapper::getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w, int *h)
+{
+ if (memSize)
+ *memSize = imgWrapper->imgMemSize;
+ if (w)
+ *w = imgWrapper->imgSize.width();
+ if (h)
+ *h = imgWrapper->imgSize.height();
+ return imgWrapper->imgFd;
+}
+
+void VulkanWrapper::freeTextureImage(VulkanImageWrapper *imageWrapper)
+{
+ d_ptr->freeTextureImage(imageWrapper);
+}
+
+QT_END_NAMESPACE
diff --git a/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h
new file mode 100644
index 000000000..541618fb3
--- /dev/null
+++ b/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VULKANWRAPPER_H
+#define VULKANWRAPPER_H
+
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+class VulkanWrapper;
+struct VulkanImageWrapper;
+class VulkanWrapperPrivate;
+
+class QOpenGLContext;
+class QImage;
+
+class VulkanWrapper
+{
+public:
+ VulkanWrapper(QOpenGLContext *glContext);
+
+ VulkanImageWrapper *createTextureImage(const QImage &img);
+ VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat);
+ int getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w = nullptr, int *h = nullptr);
+ void freeTextureImage(VulkanImageWrapper *imageWrapper);
+
+private:
+ VulkanWrapperPrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // VULKANWRAPPER_H
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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqmlengine.h>
+
+#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<QWaylandTextureSharingExtensionQuickExtension>("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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqmlengine.h>
+
+#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 <QFile>
+#include <QDebug>
+#include <qopenglfunctions.h>
+#include <QQuickWindow>
+
+#include <QtWaylandClient/private/qwaylandintegration_p.h>
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <QtGui/QWindow>
+#include <QOpenGLTexture>
+#include <QImageReader>
+
+#include <QTimer>
+
+#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<QtWaylandClient::QWaylandServerBuffer *>(m_buffer));
+ }
+
+private:
+ const QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr;
+ QString m_id;
+ QPointer<SharedTextureRegistry> 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 <QOpenGLFunctions>
+#include <QQuickImageProvider>
+#include <QtQuick/QSGTexture>
+#include <QScopedPointer>
+#include <QHash>
+
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+
+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<QString, QtWaylandClient::QWaylandServerBuffer *> 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 <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtWaylandClient/private/qwaylandintegration_p.h>
+#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
+#include <QtGui/QGuiApplication>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/QWindow>
+#include <QtGui/QPlatformSurfaceEvent>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+TextureSharingExtension::TextureSharingExtension()
+ : QWaylandClientExtensionTemplate(/* Supported protocol version */ 1 )
+{
+ auto *wayland_integration = static_cast<QtWaylandClient::QWaylandIntegration *>(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 <qpa/qwindowsysteminterface.h>
+#include <QtWaylandClient/private/qwayland-wayland.h>
+#include <QtWaylandClient/qwaylandclientextension.h>
+#include "qwayland-qt-texture-sharing-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+ class QWaylandServerBuffer;
+ class QWaylandServerBufferIntegration;
+};
+
+class TextureSharingExtension : public QWaylandClientExtensionTemplate<TextureSharingExtension>
+ , 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 <QtWaylandClient/private/qwaylandserverbufferintegrationplugin_p.h>
+#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 59ea91414..32edf1f15 100644
--- a/src/plugins/hardwareintegration/compositor/compositor.pro
+++ b/src/plugins/hardwareintegration/compositor/compositor.pro
@@ -20,6 +20,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 <QtWaylandCompositor/private/qwlserverbufferintegrationplugin_p.h>
+#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/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
index 980e4a601..99b478d98 100644
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
@@ -342,7 +342,7 @@ void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states)
if (m_toplevel)
m_toplevel->requestWindowStates(states);
else
- qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states";
+ qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel.";
}
void QWaylandXdgSurfaceV6::setToplevel()
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index f55298134..026bb56a0 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
@@ -377,7 +377,7 @@ void QWaylandXdgSurface::requestWindowStates(Qt::WindowStates states)
if (m_toplevel)
m_toplevel->requestWindowStates(states);
else
- qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states";
+ qCDebug(lcQpaWayland) << "Ignoring window states requested by non-toplevel zxdg_surface_v6.";
}
void QWaylandXdgSurface::setToplevel()
diff --git a/src/qtwaylandscanner/qtwaylandscanner.cpp b/src/qtwaylandscanner/qtwaylandscanner.cpp
index 56045e880..7f3dc5ad6 100644
--- a/src/qtwaylandscanner/qtwaylandscanner.cpp
+++ b/src/qtwaylandscanner/qtwaylandscanner.cpp
@@ -986,6 +986,7 @@ bool Scanner::process()
printf("\n");
printf(" struct ::%s *object() { return m_%s; }\n", interfaceName, interfaceName);
printf(" const struct ::%s *object() const { return m_%s; }\n", interfaceName, interfaceName);
+ printf(" static %s *fromObject(struct ::%s *object);\n", interfaceName, interfaceName);
printf("\n");
printf(" bool isInitialized() const;\n");
printf("\n");
@@ -1130,6 +1131,16 @@ bool Scanner::process()
printf(" }\n");
printf("\n");
+ printf(" %s *%s::fromObject(struct ::%s *object)\n", interfaceName, interfaceName, interfaceName);
+ printf(" {\n");
+ if (hasEvents) {
+ printf(" if (wl_proxy_get_listener((struct ::wl_proxy *)object) != (void *)&m_%s_listener)\n", interfaceName);
+ printf(" return nullptr;\n");
+ }
+ printf(" return static_cast<%s *>(%s_get_user_data(object));\n", interfaceName, interfaceName);
+ printf(" }\n");
+ printf("\n");
+
printf(" bool %s::isInitialized() const\n", interfaceName);
printf(" {\n");
printf(" return m_%s != nullptr;\n", interfaceName);
diff --git a/src/shared/qwaylandsharedmemoryformathelper_p.h b/src/shared/qwaylandsharedmemoryformathelper_p.h
index 85905c02f..72cc8401c 100644
--- a/src/shared/qwaylandsharedmemoryformathelper_p.h
+++ b/src/shared/qwaylandsharedmemoryformathelper_p.h
@@ -51,7 +51,26 @@ class QWaylandSharedMemoryFormatHelper
{
public:
static inline wl_shm_format fromQImageFormat(QImage::Format format);
- static inline QImage::Format fromWaylandShmFormat(wl_shm_format format);
+ static inline QImage::Format fromWaylandShmFormat(wl_shm_format format)
+ {
+ switch (format) {
+ case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32;
+ case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied;
+ case WL_SHM_FORMAT_RGB565: return QImage::Format_RGB16;
+ case WL_SHM_FORMAT_XRGB1555: return QImage::Format_RGB555;
+ case WL_SHM_FORMAT_RGB888: return QImage::Format_RGB888;
+ case WL_SHM_FORMAT_XRGB4444: return QImage::Format_RGB444;
+ case WL_SHM_FORMAT_ARGB4444: return QImage::Format_ARGB4444_Premultiplied;
+ case WL_SHM_FORMAT_XBGR8888: return QImage::Format_RGBX8888;
+ case WL_SHM_FORMAT_ABGR8888: return QImage::Format_RGBA8888_Premultiplied;
+ case WL_SHM_FORMAT_XBGR2101010: return QImage::Format_BGR30;
+ case WL_SHM_FORMAT_ABGR2101010: return QImage::Format_A2BGR30_Premultiplied;
+ case WL_SHM_FORMAT_XRGB2101010: return QImage::Format_RGB30;
+ case WL_SHM_FORMAT_ARGB2101010: return QImage::Format_A2RGB30_Premultiplied;
+ case WL_SHM_FORMAT_C8: return QImage::Format_Alpha8;
+ default: return QImage::Format_Invalid;
+ }
+ }
static inline QVector<wl_shm_format> supportedWaylandFormats();
private:
@@ -108,16 +127,6 @@ wl_shm_format QWaylandSharedMemoryFormatHelper::fromQImageFormat(QImage::Format
return array.data[format];
}
-QImage::Format QWaylandSharedMemoryFormatHelper::fromWaylandShmFormat(wl_shm_format format)
-{
- Array array = getData();
- for (size_t i = 0; i < array.size; i++) {
- if (array.data[i] == format)
- return QImage::Format(i);
- }
- return QImage::Format_Invalid;
-}
-
QVector<wl_shm_format> QWaylandSharedMemoryFormatHelper::supportedWaylandFormats()
{
QVector<wl_shm_format> retFormats;
diff --git a/src/src.pro b/src/src.pro
index 031ff885c..d4244de33 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -28,7 +28,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 a06caa377..1aa935c33 100644
--- a/sync.profile
+++ b/sync.profile
@@ -59,6 +59,7 @@
"^qwayland-server-qt-windowmanager.h",
"^qwayland-server-qt-key-unstable-v1.h",
"^qwayland-server-scaler.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",
@@ -72,6 +73,7 @@
"^wayland-qt-windowmanager-server-protocol.h",
"^wayland-qt-key-unstable-v1-server-protocol.h",
"^wayland-scaler-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-viewporter-server-protocol.h",
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 4ae3b4f65..d81339e86 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,11 +1,10 @@
TEMPLATE=subdirs
QT_FOR_CONFIG += waylandclient-private
-qtConfig(wayland-client): \
- SUBDIRS += client
+!qtHaveModule(waylandcompositor): \
+ return()
-qtConfig(wayland-client):qtHaveModule(waylandcompositor): \
- SUBDIRS += cmake
+SUBDIRS += compositor
-qtHaveModule(waylandcompositor): \
- SUBDIRS += compositor
+qtConfig(wayland-client): \
+ SUBDIRS += client cmake
diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro
index 051cb4e3d..06c1cb877 100644
--- a/tests/auto/client/client.pro
+++ b/tests/auto/client/client.pro
@@ -9,6 +9,7 @@ SUBDIRS += \
seatv4 \
surface \
wl_connect \
+ xdgdecorationv1 \
xdgoutput \
xdgshell \
xdgshellv6
diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h
index 75ef1eaea..05bf32c8d 100644
--- a/tests/auto/client/shared/mockcompositor.h
+++ b/tests/auto/client/shared/mockcompositor.h
@@ -78,6 +78,7 @@ public:
int main(int argc, char **argv) \
{ \
setenv("XDG_RUNTIME_DIR", ".", 1); \
+ setenv("XDG_CURRENT_DESKTOP", "qtwaylandtests", 1); \
setenv("QT_QPA_PLATFORM", "wayland", 1); \
test tc; \
QGuiApplication app(argc, argv); \
diff --git a/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp b/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp
new file mode 100644
index 000000000..386713cf5
--- /dev/null
+++ b/tests/auto/client/xdgdecorationv1/tst_xdgdecorationv1.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mockcompositor.h"
+
+#include <qwayland-server-xdg-decoration-unstable-v1.h>
+
+#include <QtGui/QRasterWindow>
+#include <QtGui/QOpenGLWindow>
+#include <QtGui/QClipboard>
+#include <QtCore/private/qcore_unix_p.h>
+
+#include <fcntl.h>
+
+using namespace MockCompositor;
+
+constexpr int xdgDecorationVersion = 1; // protocol VERSION, not the name suffix (_v1)
+
+class XdgDecorationManagerV1;
+class XdgToplevelDecorationV1 : public QObject, public QtWaylandServer::zxdg_toplevel_decoration_v1
+{
+ Q_OBJECT
+public:
+ explicit XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager, XdgToplevel *toplevel, int id, int version)
+ : zxdg_toplevel_decoration_v1(toplevel->resource()->client(), id, version)
+ , m_manager(manager)
+ , m_toplevel(toplevel)
+ {
+ }
+ void sendConfigure(mode mode)
+ {
+ if (!m_configureSent) {
+ // Attaching buffers before the configure is a protocol error
+ QVERIFY(!m_toplevel->surface()->m_pending.buffer);
+ QVERIFY(!m_toplevel->surface()->m_committed.buffer);
+ }
+ send_configure(mode);
+ m_configureSent = true;
+ }
+ void zxdg_toplevel_decoration_v1_destroy(Resource *resource) override
+ {
+ wl_resource_destroy(resource->handle);
+ }
+ void zxdg_toplevel_decoration_v1_destroy_resource(Resource *resource) override;
+ void zxdg_toplevel_decoration_v1_set_mode(Resource *resource, uint32_t mode) override
+ {
+ Q_UNUSED(resource);
+ m_unsetModeRequested = false;
+ m_requestedMode = XdgToplevelDecorationV1::mode(mode);
+ }
+ void zxdg_toplevel_decoration_v1_unset_mode(Resource *resource) override
+ {
+ Q_UNUSED(resource);
+ m_unsetModeRequested = true;
+ m_requestedMode = mode(0);
+ }
+ XdgDecorationManagerV1 *m_manager = nullptr;
+ XdgToplevel *m_toplevel = nullptr;
+ mode m_requestedMode = mode(0);
+ bool m_unsetModeRequested = false;
+ bool m_configureSent = false;
+};
+
+class XdgDecorationManagerV1 : public Global, public QtWaylandServer::zxdg_decoration_manager_v1
+{
+ Q_OBJECT
+public:
+ explicit XdgDecorationManagerV1(CoreCompositor *compositor, int version = 1)
+ : QtWaylandServer::zxdg_decoration_manager_v1(compositor->m_display, version)
+ , m_version(version)
+ {}
+ bool isClean() override { return m_decorations.empty(); }
+ XdgToplevelDecorationV1 *decorationFor(XdgToplevel *toplevel)
+ {
+ return m_decorations.value(toplevel, nullptr);
+ }
+
+ int m_version = 1; // TODO: Remove on libwayland upgrade
+ QMap<XdgToplevel *, XdgToplevelDecorationV1 *> m_decorations;
+
+protected:
+ void zxdg_decoration_manager_v1_destroy(Resource *resource) override
+ {
+ //TODO: Should the decorations be destroyed at this point?
+ wl_resource_destroy(resource->handle);
+ }
+
+ void zxdg_decoration_manager_v1_get_toplevel_decoration(Resource *resource, uint32_t id, ::wl_resource *toplevelResource) override
+ {
+ auto *toplevel = fromResource<XdgToplevel>(toplevelResource);
+ QVERIFY(toplevel);
+ QVERIFY(!decorationFor(toplevel));
+
+ // Attaching buffers before the configure is a protocol error
+ QVERIFY(!toplevel->surface()->m_pending.buffer);
+ QVERIFY(!toplevel->surface()->m_committed.buffer);
+
+ m_decorations[toplevel] = new XdgToplevelDecorationV1(this, toplevel, id, resource->version());
+ }
+};
+
+void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_destroy_resource(QtWaylandServer::zxdg_toplevel_decoration_v1::Resource *resource)
+{
+ Q_UNUSED(resource);
+ int removed = m_manager->m_decorations.remove(m_toplevel);
+ Q_ASSERT(removed == 1);
+ delete this;
+}
+
+class XdgDecorationCompositor : public DefaultCompositor {
+public:
+ explicit XdgDecorationCompositor()
+ {
+ exec([this] {
+ m_config.autoConfigure = true;
+ add<XdgDecorationManagerV1>(xdgDecorationVersion);
+ });
+ }
+ XdgToplevelDecorationV1 *toplevelDecoration(int i = 0) {
+ return get<XdgDecorationManagerV1>()->decorationFor(xdgToplevel(i));
+ }
+};
+
+class tst_xdgdecorationv1 : public QObject, private XdgDecorationCompositor
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); }
+ void clientSidePreferredByCompositor();
+};
+
+void tst_xdgdecorationv1::initTestCase()
+{
+ if (qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_WINDOWDECORATION"))
+ QSKIP("This test doesn't make sense when QT_WAYLAND_DISABLE_WINDOWDECORATION is set in the environment");
+}
+
+void tst_xdgdecorationv1::clientSidePreferredByCompositor()
+{
+ QRasterWindow window;
+ window.show();
+ QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().size(), 1);
+ QCOMPOSITOR_TRY_COMPARE(get<XdgDecorationManagerV1>()->resourceMap().first()->version(), xdgDecorationVersion);
+ QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()); // The client creates a toplevel object
+
+ // Check that we don't assume decorations before the server has configured them
+ QVERIFY(window.frameMargins().isNull());
+
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
+ QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested);
+ QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure
+ exec([=] {
+ toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side);
+ xdgToplevel()->sendCompleteConfigure();
+ });
+ QTRY_VERIFY(!window.frameMargins().isNull());
+}
+
+QCOMPOSITOR_TEST_MAIN(tst_xdgdecorationv1)
+#include "tst_xdgdecorationv1.moc"
diff --git a/tests/auto/client/xdgdecorationv1/xdgdecorationv1.pro b/tests/auto/client/xdgdecorationv1/xdgdecorationv1.pro
new file mode 100644
index 000000000..0b5537205
--- /dev/null
+++ b/tests/auto/client/xdgdecorationv1/xdgdecorationv1.pro
@@ -0,0 +1,7 @@
+include (../shared/shared.pri)
+
+WAYLANDSERVERSOURCES += \
+ $$PWD/../../../../src/3rdparty/protocol/xdg-decoration-unstable-v1.xml
+
+TARGET = tst_xdgdecorationv1
+SOURCES += tst_xdgdecorationv1.cpp