diff options
Diffstat (limited to 'src/imports/texture-sharing/sharedtextureprovider.cpp')
-rw-r--r-- | src/imports/texture-sharing/sharedtextureprovider.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
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" |