7#include <QWaylandSurface>
11#include <QQuickWindow>
17#include <QtGui/private/qtexturefilereader_p.h>
19#include <QtOpenGL/QOpenGLTexture>
20#include <QtGui/QImageReader>
22#include <QtQuick/QSGTexture>
28class SharedTextureFactory :
public QQuickTextureFactory
31 SharedTextureFactory(
const QtWayland::ServerBuffer *buffer)
36 ~SharedTextureFactory() override
38 if (m_buffer && !QCoreApplication::closingDown())
39 const_cast<QtWayland::ServerBuffer*>(m_buffer)->releaseOpenGlTexture();
42 QSize textureSize()
const override
44 return m_buffer ? m_buffer->size() : QSize();
47 int textureByteCount()
const override
49 return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
52 QSGTexture *createTexture(QQuickWindow *window)
const override
54 if (m_buffer !=
nullptr) {
55 QOpenGLTexture *texture =
const_cast<QtWayland::ServerBuffer *>(m_buffer)->toOpenGlTexture();
56 return QNativeInterface::QSGOpenGLTexture::fromNative(texture->textureId(),
59 QQuickWindow::TextureHasAlphaChannel);
66 const QtWayland::ServerBuffer *m_buffer =
nullptr;
119 QWaylandTextureSharingExtension *m_extension =
nullptr;
120 mutable QString m_errorString;
121 QtWayland::ServerBuffer *m_buffer =
nullptr;
124QWaylandSharedTextureProvider::QWaylandSharedTextureProvider()
128QWaylandSharedTextureProvider::~QWaylandSharedTextureProvider()
132QQuickImageResponse *QWaylandSharedTextureProvider::requestImageResponse(
const QString &id,
const QSize &requestedSize)
134 Q_UNUSED(requestedSize);
138 auto *extension = QWaylandTextureSharingExtension::self();
139 auto *response =
new SharedTextureImageResponse(extension, id);
141 m_pendingResponses << response;
146void QWaylandSharedTextureProvider::setExtensionReady(QWaylandTextureSharingExtension *extension)
148 for (
auto *response : std::as_const(m_pendingResponses))
149 response->doRequest(extension);
150 m_pendingResponses.clear();
151 m_pendingResponses.squeeze();
154QWaylandTextureSharingExtension *QWaylandTextureSharingExtension::s_self =
nullptr;
156QWaylandTextureSharingExtension::QWaylandTextureSharingExtension()
161QWaylandTextureSharingExtension::QWaylandTextureSharingExtension(QWaylandCompositor *compositor)
162 :QWaylandCompositorExtensionTemplate(compositor)
167QWaylandTextureSharingExtension::~QWaylandTextureSharingExtension()
172 for (
auto b : std::as_const(m_server_buffers))
179void QWaylandTextureSharingExtension::setImageSearchPath(
const QString &path)
181 m_image_dirs = path.split(QLatin1Char(
';'));
183 for (
auto it = m_image_dirs.begin(); it != m_image_dirs.end(); ++it)
184 if (!(*it).endsWith(QLatin1Char(
'/')))
185 (*it) += QLatin1Char(
'/');
188void QWaylandTextureSharingExtension::initialize()
190 QWaylandCompositorExtensionTemplate::initialize();
191 QWaylandCompositor *compositor =
static_cast<QWaylandCompositor *>(extensionContainer());
192 init(compositor->display(), 1);
194 QString image_search_path = qEnvironmentVariable(
"QT_WAYLAND_SHAREDTEXTURE_SEARCH_PATH");
195 if (!image_search_path.isEmpty())
196 setImageSearchPath(image_search_path);
198 if (m_image_dirs.isEmpty())
199 m_image_dirs << QLatin1String(
":/") << QLatin1String(
"./");
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);
208 auto *ctx = QQmlEngine::contextForObject(
this);
210 QQmlEngine *engine = ctx->engine();
212 auto *provider =
static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1String(
"wlshared")));
214 provider->setExtensionReady(
this);
219QString QWaylandTextureSharingExtension::getExistingFilePath(
const QString &key)
const
225 if (key.contains(QLatin1String(
"../")))
228 for (
auto dir : std::as_const(m_image_dirs)) {
229 QString path = dir + key;
230 if (QFileInfo::exists(path))
234 for (
auto dir : std::as_const(m_image_dirs)) {
235 for (
const auto &ext : m_image_suffixes) {
236 QString fp = dir + key + ext;
238 if (QFileInfo::exists(fp))
245QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getBuffer(
const QString &key)
247 if (!initServerBufferIntegration())
252 QtWayland::ServerBuffer *buffer =
nullptr;
254 if ((buffer = m_server_buffers.value(key).buffer))
257 QByteArray pixelData;
259 uint glInternalFormat = GL_NONE;
261 if (customPixelData(key, &pixelData, &size, &glInternalFormat)) {
262 if (!pixelData.isEmpty()) {
263 buffer = m_server_buffer_integration->createServerBufferFromData(pixelData, size, glInternalFormat);
265 qWarning() <<
"QWaylandTextureSharingExtension: could not create buffer from custom data for key:" << key;
268 QString pathName = getExistingFilePath(key);
270 if (pathName.isEmpty())
273 buffer = getCompressedBuffer(pathName);
277 QImage img(pathName);
279 img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
280 buffer = m_server_buffer_integration->createServerBufferFromImage(img, QtWayland::ServerBuffer::RGBA32);
286 m_server_buffers.insert(key, BufferInfo(buffer));
294void QWaylandTextureSharingExtension::requestBuffer(
const QString &key)
298 if (thread() != QThread::currentThread())
299 qWarning(
"QWaylandTextureSharingExtension::requestBuffer() called from outside main thread: possible race condition");
301 auto *buffer = getBuffer(key);
304 m_server_buffers[key].usedLocally =
true;
308 emit bufferResult(key, buffer);
311void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_request_image(Resource *resource,
const QString &key)
314 auto *buffer = getBuffer(key);
316 struct ::wl_client *client = resource->client();
317 struct ::wl_resource *buffer_resource = buffer->resourceForClient(client);
320 send_provide_buffer(resource->handle, buffer_resource, key);
322 qWarning() <<
"QWaylandTextureSharingExtension: no buffer resource for client";
324 send_image_failed(resource->handle, key, QString());
329void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_abandon_image(Resource *resource,
const QString &key)
334 QTimer::singleShot(100,
this, &QWaylandTextureSharingExtension::cleanupBuffers);
338void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_destroy_resource(Resource *resource)
343 QTimer::singleShot(1000,
this, &QWaylandTextureSharingExtension::cleanupBuffers);
346bool QWaylandTextureSharingExtension::initServerBufferIntegration()
348 if (!m_server_buffer_integration) {
349 QWaylandCompositor *compositor =
static_cast<QWaylandCompositor *>(extensionContainer());
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.");
362QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getCompressedBuffer(
const QString &pathName)
365 if (!f.open(QIODevice::ReadOnly))
368 QTextureFileReader r(&f, pathName);
373 QTextureFileData td(r.read());
378 qWarning() <<
"VulkanServerBufferIntegration:" << pathName <<
"not valid compressed texture";
382 return m_server_buffer_integration->createServerBufferFromData(td.getDataView(), td.size(),
383 td.glInternalFormat());
386void QWaylandTextureSharingExtension::cleanupBuffers()
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()) {
392 it = m_server_buffers.erase(it);
401void QWaylandTextureSharingExtension::dumpBufferInfo()
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 ;
410#include "moc_qwltexturesharingextension_p.cpp"
412#include "qwltexturesharingextension.moc"