1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#include "sharedtextureprovider_p.h"
6
7#include <QFile>
8#include <QDebug>
9#include <qopenglfunctions.h>
10#include <QQuickWindow>
11
12#include <QtWaylandClient/private/qwaylandintegration_p.h>
13#include <QtWaylandClient/private/qwaylandserverbufferintegration_p.h>
14#include <QtGui/QGuiApplication>
15#include <QtGui/private/qguiapplication_p.h>
16#include <QtQuick/private/qsgrhisupport_p.h>
17#include <QtGui/qpa/qplatformnativeinterface.h>
18#include <QtGui/QWindow>
19#include <QOpenGLTexture>
20#include <QImageReader>
21
22#include <QTimer>
23
24#include "texturesharingextension_p.h"
25
26QT_BEGIN_NAMESPACE
27
28class SharedTextureFactory : public QQuickTextureFactory
29{
30public:
31 SharedTextureFactory(const QtWaylandClient::QWaylandServerBuffer *buffer, const QString &id, SharedTextureRegistry *registry)
32 : m_buffer(buffer), m_id(id), m_registry(registry)
33 {
34 }
35
36 ~SharedTextureFactory() override
37 {
38 //qDebug() << "====> DESTRUCTOR SharedTextureFactory" << this;
39 if (m_registry)
40 m_registry->abandonBuffer(id: m_id);
41 delete m_buffer; // TODO: make sure we are not keeping references to this elsewhere
42 //qDebug() << "buffer deleted";
43 }
44
45 QSize textureSize() const override
46 {
47 return m_buffer ? m_buffer->size() : QSize();
48 }
49
50 int textureByteCount() const override
51 {
52 return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
53 }
54
55 QSGTexture *createTexture(QQuickWindow *window) const override
56 {
57 if (m_buffer != nullptr) {
58 QOpenGLTexture *texture = const_cast<QtWaylandClient::QWaylandServerBuffer *>(m_buffer)->toOpenGlTexture();
59 return QNativeInterface::QSGOpenGLTexture::fromNative(textureId: texture->textureId(),
60 window,
61 size: m_buffer->size(),
62 options: QQuickWindow::TextureHasAlphaChannel);
63 }
64 return nullptr;
65 }
66
67private:
68 const QtWaylandClient::QWaylandServerBuffer *m_buffer = nullptr;
69 QString m_id;
70 QPointer<SharedTextureRegistry> m_registry;
71};
72
73
74SharedTextureRegistry::SharedTextureRegistry()
75 : m_extension(new TextureSharingExtension)
76{
77 connect(sender: m_extension, signal: &TextureSharingExtension::bufferReceived, context: this, slot: &SharedTextureRegistry::receiveBuffer);
78 connect(sender: m_extension, signal: &TextureSharingExtension::activeChanged, context: this, slot: &SharedTextureRegistry::handleExtensionActive);
79}
80
81SharedTextureRegistry::~SharedTextureRegistry()
82{
83 delete m_extension;
84}
85
86const QtWaylandClient::QWaylandServerBuffer *SharedTextureRegistry::bufferForId(const QString &id) const
87{
88 return m_buffers.value(key: id);
89}
90
91void SharedTextureRegistry::requestBuffer(const QString &id)
92{
93 if (!m_extension->isActive()) {
94 //qDebug() << "Extension not active, adding" << id << "to queue";
95 m_pendingBuffers << id;
96 return;
97 }
98 m_extension->requestImage(key: id);
99}
100
101void SharedTextureRegistry::abandonBuffer(const QString &id)
102{
103 m_buffers.remove(key: id);
104 m_extension->abandonImage(key: id);
105}
106
107
108void SharedTextureRegistry::handleExtensionActive()
109{
110 //qDebug() << "handleExtensionActive, queue:" << m_pendingBuffers;
111 if (m_extension->isActive())
112 while (!m_pendingBuffers.isEmpty())
113 requestBuffer(id: m_pendingBuffers.takeFirst());
114}
115
116bool SharedTextureRegistry::preinitialize()
117{
118 if (QSGRhiSupport::instance()->rhiBackend() != QRhi::OpenGLES2) {
119 qWarning() << "The shared-texture extension is only supported on OpenGL. Use QQuickWindow::setSceneGraphBackend() to override the default.";
120 return false;
121 }
122
123 auto *serverBufferIntegration = QGuiApplicationPrivate::platformIntegration()->nativeInterface()->nativeResourceForIntegration(resource: "server_buffer_integration");
124
125 if (!serverBufferIntegration) {
126 qWarning() << "Wayland Server Buffer Integration not available.";
127 return false;
128 }
129
130 return true;
131}
132
133void SharedTextureRegistry::receiveBuffer(QtWaylandClient::QWaylandServerBuffer *buffer, const QString& id)
134{
135 //qDebug() << "ReceiveBuffer for id" << id;
136 if (buffer)
137 m_buffers.insert(key: id, value: buffer);
138 emit replyReceived(id);
139}
140
141class SharedTextureImageResponse : public QQuickImageResponse
142{
143 Q_OBJECT
144public:
145 SharedTextureImageResponse(SharedTextureRegistry *registry, const QString &id)
146 : m_id(id), m_registry(registry)
147 {
148 if (!m_registry || m_registry->bufferForId(id)) {
149 // Shortcut: no server roundtrip needed, just let the event loop call the slot
150 QMetaObject::invokeMethod(obj: this, member: "doResponse", c: Qt::QueuedConnection, Q_ARG(QString, id));
151
152 } else {
153 // TBD: timeout?
154 connect(sender: registry, signal: &SharedTextureRegistry::replyReceived, context: this, slot: &SharedTextureImageResponse::doResponse);
155 registry->requestBuffer(id);
156 }
157 }
158
159 QQuickTextureFactory *textureFactory() const override
160 {
161 if (m_registry) {
162 const QtWaylandClient::QWaylandServerBuffer *buffer = m_registry->bufferForId(id: m_id);
163 if (buffer) {
164 //qDebug() << "Creating shared buffer texture for" << m_id;
165 return new SharedTextureFactory(buffer, m_id, m_registry);
166 }
167 //qDebug() << "Shared buffer NOT found for" << m_id;
168 }
169
170 // No shared buffer, do fallback
171 QString fbPath = fallbackPath();
172 if (fbPath.isEmpty()) {
173 m_errorString = QStringLiteral("Shared buffer not found, and no fallback path set.");
174 return nullptr;
175 }
176
177 QImageReader reader(fbPath + m_id);
178 QImage img = reader.read();
179 if (img.isNull()) {
180 qWarning() << "Could not load local image from id/path" << reader.fileName();
181 m_errorString = QStringLiteral("Shared buffer not found, and fallback local file loading failed: ") + reader.errorString();
182 return nullptr;
183 }
184 return QQuickTextureFactory::textureFactoryForImage(image: img);
185 }
186
187 QString errorString() const override
188 {
189 return m_errorString;
190 }
191
192 static QString fallbackPath()
193 {
194 static QString fbPath;
195 static bool isInit = false;
196 if (!isInit) {
197 isInit = true;
198 QByteArray envVal = qgetenv(varName: "QT_SHAREDTEXTURE_FALLBACK_DIR");
199 if (!envVal.isEmpty()) {
200 fbPath = QString::fromLocal8Bit(ba: envVal);
201 if (!fbPath.endsWith(c: QLatin1Char('/')))
202 fbPath.append(c: QLatin1Char('/'));
203 }
204 }
205 return fbPath;
206 }
207
208
209public slots:
210 void doResponse(const QString &key) {
211 if (key != m_id)
212 return; // not our buffer
213
214 // No need to be called again
215 if (m_registry)
216 disconnect(sender: m_registry, signal: &SharedTextureRegistry::replyReceived, receiver: this, slot: &SharedTextureImageResponse::doResponse);
217
218 emit finished();
219 }
220
221private:
222 QString m_id;
223 SharedTextureRegistry *m_registry = nullptr;
224 mutable QString m_errorString;
225};
226
227
228SharedTextureProvider::SharedTextureProvider()
229{
230 m_sharingAvailable = SharedTextureRegistry::preinitialize();
231 if (!m_sharingAvailable) {
232 if (SharedTextureImageResponse::fallbackPath().isEmpty())
233 qWarning() << "Shared buffer images not available, and no fallback directory set.";
234 else
235 qWarning() << "Shared buffer images not available, will fallback to local image files from" << SharedTextureImageResponse::fallbackPath();
236 }
237}
238
239SharedTextureProvider::~SharedTextureProvider()
240{
241 delete m_registry;
242}
243
244QQuickImageResponse *SharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
245{
246 Q_UNUSED(requestedSize);
247
248 //qDebug() << "Provider: got request for" << id;
249
250 if (m_sharingAvailable && !m_registry)
251 m_registry = new SharedTextureRegistry;
252
253 return new SharedTextureImageResponse(m_registry, id);
254}
255
256QT_END_NAMESPACE
257
258#include "moc_sharedtextureprovider_p.cpp"
259
260#include "sharedtextureprovider.moc"
261

source code of qtwayland/src/imports/texture-sharing/sharedtextureprovider.cpp