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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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