Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwltexturesharingextension.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
6
7#include <QWaylandSurface>
8
9#include <QDebug>
10
11#include <QQuickWindow>
12
13#include <QPainter>
14#include <QPen>
15#include <QTimer>
16
17#include <QtGui/private/qtexturefilereader_p.h>
18
19#include <QtOpenGL/QOpenGLTexture>
20#include <QtGui/QImageReader>
21
22#include <QtQuick/QSGTexture>
23#include <QQmlContext>
24#include <QThread>
25
26QT_BEGIN_NAMESPACE
27
28class SharedTextureFactory : public QQuickTextureFactory
29{
30public:
31 SharedTextureFactory(const QtWayland::ServerBuffer *buffer)
32 : m_buffer(buffer)
33 {
34 }
35
36 ~SharedTextureFactory() override
37 {
38 if (m_buffer && !QCoreApplication::closingDown())
39 const_cast<QtWayland::ServerBuffer*>(m_buffer)->releaseOpenGlTexture();
40 }
41
42 QSize textureSize() const override
43 {
44 return m_buffer ? m_buffer->size() : QSize();
45 }
46
47 int textureByteCount() const override
48 {
49 return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
50 }
51
52 QSGTexture *createTexture(QQuickWindow *window) const override
53 {
54 if (m_buffer != nullptr) {
55 QOpenGLTexture *texture = const_cast<QtWayland::ServerBuffer *>(m_buffer)->toOpenGlTexture();
56 return QNativeInterface::QSGOpenGLTexture::fromNative(texture->textureId(),
57 window,
58 m_buffer->size(),
59 QQuickWindow::TextureHasAlphaChannel);
60 }
61
62 return nullptr;
63 }
64
65private:
66 const QtWayland::ServerBuffer *m_buffer = nullptr;
67};
68
70{
72public:
79
86
88 {
89 if (m_buffer) {
90// qDebug() << "Creating shared buffer texture for" << m_id;
92 }
93// qDebug() << "Shared buffer NOT found for" << m_id;
94 m_errorString = QLatin1String("Shared buffer not found");
95 return nullptr;
96 }
97
99 {
100 return m_errorString;
101 }
102
103public Q_SLOTS:
105 {
106 if (key != m_id)
107 return; //somebody else's texture
108
110
111 if (m_extension)
113
114 emit finished();
115 }
116
117private:
119 QWaylandTextureSharingExtension *m_extension = nullptr;
120 mutable QString m_errorString;
121 QtWayland::ServerBuffer *m_buffer = nullptr;
122};
123
124QWaylandSharedTextureProvider::QWaylandSharedTextureProvider()
125{
126}
127
128QWaylandSharedTextureProvider::~QWaylandSharedTextureProvider()
129{
130}
131
132QQuickImageResponse *QWaylandSharedTextureProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
133{
134 Q_UNUSED(requestedSize);
135
136// qDebug() << "Provider: got request for" << id;
137
138 auto *extension = QWaylandTextureSharingExtension::self();
139 auto *response = new SharedTextureImageResponse(extension, id);
140 if (!extension)
141 m_pendingResponses << response;
142
143 return response;
144}
145
146void QWaylandSharedTextureProvider::setExtensionReady(QWaylandTextureSharingExtension *extension)
147{
148 for (auto *response : std::as_const(m_pendingResponses))
149 response->doRequest(extension);
150 m_pendingResponses.clear();
151 m_pendingResponses.squeeze();
152}
153
154QWaylandTextureSharingExtension *QWaylandTextureSharingExtension::s_self = nullptr; // theoretical race conditions, but OK as long as we don't delete it while we are running
155
156QWaylandTextureSharingExtension::QWaylandTextureSharingExtension()
157{
158 s_self = this;
159}
160
161QWaylandTextureSharingExtension::QWaylandTextureSharingExtension(QWaylandCompositor *compositor)
162 :QWaylandCompositorExtensionTemplate(compositor)
163{
164 s_self = this;
165}
166
167QWaylandTextureSharingExtension::~QWaylandTextureSharingExtension()
168{
169 //qDebug() << Q_FUNC_INFO;
170 //dumpBufferInfo();
171
172 for (auto b : std::as_const(m_server_buffers))
173 delete b.buffer;
174
175 if (s_self == this)
176 s_self = nullptr;
177}
178
179void QWaylandTextureSharingExtension::setImageSearchPath(const QString &path)
180{
181 m_image_dirs = path.split(QLatin1Char(';'));
182
183 for (auto it = m_image_dirs.begin(); it != m_image_dirs.end(); ++it)
184 if (!(*it).endsWith(QLatin1Char('/')))
185 (*it) += QLatin1Char('/');
186}
187
188void QWaylandTextureSharingExtension::initialize()
189{
190 QWaylandCompositorExtensionTemplate::initialize();
191 QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
192 init(compositor->display(), 1);
193
194 QString image_search_path = qEnvironmentVariable("QT_WAYLAND_SHAREDTEXTURE_SEARCH_PATH");
195 if (!image_search_path.isEmpty())
196 setImageSearchPath(image_search_path);
197
198 if (m_image_dirs.isEmpty())
199 m_image_dirs << QLatin1String(":/") << QLatin1String("./");
200
201 auto suffixes = QTextureFileReader::supportedFileFormats();
202 suffixes.append(QImageReader::supportedImageFormats());
203 for (const auto &ext : std::as_const(suffixes))
204 m_image_suffixes << QLatin1Char('.') + QString::fromLatin1(ext);
205
206 //qDebug() << "m_image_suffixes" << m_image_suffixes << "m_image_dirs" << m_image_dirs;
207
208 auto *ctx = QQmlEngine::contextForObject(this);
209 if (ctx) {
210 QQmlEngine *engine = ctx->engine();
211 if (engine) {
212 auto *provider = static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1String("wlshared")));
213 if (provider)
214 provider->setExtensionReady(this);
215 }
216 }
217}
218
219QString QWaylandTextureSharingExtension::getExistingFilePath(const QString &key) const
220{
221 // The default search path blocks absolute pathnames, but this does not prevent relative
222 // paths containing '../'. We handle that here, at the price of also blocking directory
223 // names ending with two or more dots.
224
225 if (key.contains(QLatin1String("../")))
226 return QString();
227
228 for (auto dir : std::as_const(m_image_dirs)) {
229 QString path = dir + key;
230 if (QFileInfo::exists(path))
231 return path;
232 }
233
234 for (auto dir : std::as_const(m_image_dirs)) {
235 for (const auto &ext : m_image_suffixes) {
236 QString fp = dir + key + ext;
237 //qDebug() << "trying" << fp;
238 if (QFileInfo::exists(fp))
239 return fp;
240 }
241 }
242 return QString();
243}
244
245QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getBuffer(const QString &key)
246{
247 if (!initServerBufferIntegration())
248 return nullptr;
249
250//qDebug() << "getBuffer" << key;
251
252 QtWayland::ServerBuffer *buffer = nullptr;
253
254 if ((buffer = m_server_buffers.value(key).buffer))
255 return buffer;
256
257 QByteArray pixelData;
258 QSize size;
259 uint glInternalFormat = GL_NONE;
260
261 if (customPixelData(key, &pixelData, &size, &glInternalFormat)) {
262 if (!pixelData.isEmpty()) {
263 buffer = m_server_buffer_integration->createServerBufferFromData(pixelData, size, glInternalFormat);
264 if (!buffer)
265 qWarning() << "QWaylandTextureSharingExtension: could not create buffer from custom data for key:" << key;
266 }
267 } else {
268 QString pathName = getExistingFilePath(key);
269 //qDebug() << "pathName" << pathName;
270 if (pathName.isEmpty())
271 return nullptr;
272
273 buffer = getCompressedBuffer(pathName);
274 //qDebug() << "getCompressedBuffer" << buffer;
275
276 if (!buffer) {
277 QImage img(pathName);
278 if (!img.isNull()) {
279 img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
280 buffer = m_server_buffer_integration->createServerBufferFromImage(img, QtWayland::ServerBuffer::RGBA32);
281 }
282 //qDebug() << "createServerBufferFromImage" << buffer;
283 }
284 }
285 if (buffer)
286 m_server_buffers.insert(key, BufferInfo(buffer));
287
288 //qDebug() << ">>>>" << key << buffer;
289
290 return buffer;
291}
292
293// Compositor requesting image for its own UI
294void QWaylandTextureSharingExtension::requestBuffer(const QString &key)
295{
296 //qDebug() << "requestBuffer" << key;
297
298 if (thread() != QThread::currentThread())
299 qWarning("QWaylandTextureSharingExtension::requestBuffer() called from outside main thread: possible race condition");
300
301 auto *buffer = getBuffer(key);
302
303 if (buffer)
304 m_server_buffers[key].usedLocally = true;
305
306 //dumpBufferInfo();
307
308 emit bufferResult(key, buffer);
309}
310
311void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_request_image(Resource *resource, const QString &key)
312{
313 //qDebug() << "texture_sharing_request_image" << key;
314 auto *buffer = getBuffer(key);
315 if (buffer) {
316 struct ::wl_client *client = resource->client();
317 struct ::wl_resource *buffer_resource = buffer->resourceForClient(client);
318 //qDebug() << " server_buffer resource" << buffer_resource;
319 if (buffer_resource)
320 send_provide_buffer(resource->handle, buffer_resource, key);
321 else
322 qWarning() << "QWaylandTextureSharingExtension: no buffer resource for client";
323 } else {
324 send_image_failed(resource->handle, key, QString());
325 }
326 //dumpBufferInfo();
327}
328
329void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_abandon_image(Resource *resource, const QString &key)
330{
331 Q_UNUSED(resource);
332 Q_UNUSED(key);
333// qDebug() << Q_FUNC_INFO << resource << key;
334 QTimer::singleShot(100, this, &QWaylandTextureSharingExtension::cleanupBuffers);
335}
336
337// A client has disconnected
338void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_destroy_resource(Resource *resource)
339{
340 Q_UNUSED(resource);
341// qDebug() << "texture_sharing_destroy_resource" << resource->handle << resource->handle->object.id << "client" << resource->client();
342// dumpBufferInfo();
343 QTimer::singleShot(1000, this, &QWaylandTextureSharingExtension::cleanupBuffers);
344}
345
346bool QWaylandTextureSharingExtension::initServerBufferIntegration()
347{
348 if (!m_server_buffer_integration) {
349 QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
350
351 m_server_buffer_integration = QWaylandCompositorPrivate::get(compositor)->serverBufferIntegration();
352 if (!m_server_buffer_integration) {
353 qWarning("QWaylandTextureSharingExtension initialization failed: No Server Buffer Integration");
354 if (qEnvironmentVariableIsEmpty("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"))
355 qWarning("Set the environment variable 'QT_WAYLAND_SERVER_BUFFER_INTEGRATION' to specify.");
356 return false;
357 }
358 }
359 return true;
360}
361
362QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getCompressedBuffer(const QString &pathName)
363{
364 QFile f(pathName);
365 if (!f.open(QIODevice::ReadOnly))
366 return nullptr;
367
368 QTextureFileReader r(&f, pathName);
369
370 if (!r.canRead())
371 return nullptr;
372
373 QTextureFileData td(r.read());
374
375 //qDebug() << "QWaylandTextureSharingExtension: reading compressed texture data" << td;
376
377 if (!td.isValid()) {
378 qWarning() << "VulkanServerBufferIntegration:" << pathName << "not valid compressed texture";
379 return nullptr;
380 }
381
382 return m_server_buffer_integration->createServerBufferFromData(td.getDataView(), td.size(),
383 td.glInternalFormat());
384}
385
386void QWaylandTextureSharingExtension::cleanupBuffers()
387{
388 for (auto it = m_server_buffers.begin(); it != m_server_buffers.end(); ) {
389 auto *buffer = it.value().buffer;
390 if (!it.value().usedLocally && !buffer->bufferInUse()) {
391 //qDebug() << "deleting buffer for" << it.key();
392 it = m_server_buffers.erase(it);
393 delete buffer;
394 } else {
395 ++it;
396 }
397 }
398 //dumpBufferInfo();
399}
400
401void QWaylandTextureSharingExtension::dumpBufferInfo()
402{
403 qDebug() << "shared buffers:" << m_server_buffers.size();
404 for (auto it = m_server_buffers.cbegin(); it != m_server_buffers.cend(); ++it)
405 qDebug() << " " << it.key() << ":" << it.value().buffer << "in use" << it.value().buffer->bufferInUse() << "usedLocally" << it.value().usedLocally ;
406}
407
408QT_END_NAMESPACE
409
410#include "moc_qwltexturesharingextension_p.cpp"
411
412#include "qwltexturesharingextension.moc"