6#include <QWaylandSurface>
10#include <QQuickWindow>
16#include <QtGui/private/qtexturefilereader_p.h>
18#include <QtOpenGL/QOpenGLTexture>
19#include <QtGui/QImageReader>
21#include <QtQuick/QSGTexture>
27class SharedTextureFactory :
public QQuickTextureFactory
30 SharedTextureFactory(
const QtWayland::ServerBuffer *buffer)
35 ~SharedTextureFactory() override
37 if (m_buffer && !QCoreApplication::closingDown())
38 const_cast<QtWayland::ServerBuffer*>(m_buffer)->releaseOpenGlTexture();
41 QSize textureSize()
const override
43 return m_buffer ? m_buffer->size() : QSize();
46 int textureByteCount()
const override
48 return m_buffer ? (m_buffer->size().width() * m_buffer->size().height() * 4) : 0;
51 QSGTexture *createTexture(QQuickWindow *window)
const override
53 if (m_buffer !=
nullptr) {
54 QOpenGLTexture *texture =
const_cast<QtWayland::ServerBuffer *>(m_buffer)->toOpenGlTexture();
55 return QNativeInterface::QSGOpenGLTexture::fromNative(texture->textureId(),
58 QQuickWindow::TextureHasAlphaChannel);
65 const QtWayland::ServerBuffer *m_buffer =
nullptr;
118 QWaylandTextureSharingExtension *m_extension =
nullptr;
119 mutable QString m_errorString;
120 QtWayland::ServerBuffer *m_buffer =
nullptr;
123QWaylandSharedTextureProvider::QWaylandSharedTextureProvider()
127QWaylandSharedTextureProvider::~QWaylandSharedTextureProvider()
131QQuickImageResponse *QWaylandSharedTextureProvider::requestImageResponse(
const QString &id,
const QSize &requestedSize)
133 Q_UNUSED(requestedSize);
137 auto *extension = QWaylandTextureSharingExtension::self();
138 auto *response =
new SharedTextureImageResponse(extension, id);
140 m_pendingResponses << response;
145void QWaylandSharedTextureProvider::setExtensionReady(QWaylandTextureSharingExtension *extension)
147 for (
auto *response : std::as_const(m_pendingResponses))
148 response->doRequest(extension);
149 m_pendingResponses.clear();
150 m_pendingResponses.squeeze();
153QWaylandTextureSharingExtension *QWaylandTextureSharingExtension::s_self =
nullptr;
155QWaylandTextureSharingExtension::QWaylandTextureSharingExtension()
160QWaylandTextureSharingExtension::QWaylandTextureSharingExtension(QWaylandCompositor *compositor)
161 :QWaylandCompositorExtensionTemplate(compositor)
166QWaylandTextureSharingExtension::~QWaylandTextureSharingExtension()
171 for (
auto b : m_server_buffers)
178void QWaylandTextureSharingExtension::setImageSearchPath(
const QString &path)
180 m_image_dirs = path.split(QLatin1Char(
';'));
182 for (
auto it = m_image_dirs.begin(); it != m_image_dirs.end(); ++it)
183 if (!(*it).endsWith(QLatin1Char(
'/')))
184 (*it) += QLatin1Char(
'/');
187void QWaylandTextureSharingExtension::initialize()
189 QWaylandCompositorExtensionTemplate::initialize();
190 QWaylandCompositor *compositor =
static_cast<QWaylandCompositor *>(extensionContainer());
191 init(compositor->display(), 1);
193 QString image_search_path = qEnvironmentVariable(
"QT_WAYLAND_SHAREDTEXTURE_SEARCH_PATH");
194 if (!image_search_path.isEmpty())
195 setImageSearchPath(image_search_path);
197 if (m_image_dirs.isEmpty())
198 m_image_dirs << QLatin1String(
":/") << QLatin1String(
"./");
200 auto suffixes = QTextureFileReader::supportedFileFormats();
201 suffixes.append(QImageReader::supportedImageFormats());
202 for (
auto ext : std::as_const(suffixes))
203 m_image_suffixes << QLatin1Char(
'.') + QString::fromLatin1(ext);
207 auto *ctx = QQmlEngine::contextForObject(
this);
209 QQmlEngine *engine = ctx->engine();
211 auto *provider =
static_cast<QWaylandSharedTextureProvider*>(engine->imageProvider(QLatin1String(
"wlshared")));
213 provider->setExtensionReady(
this);
218QString QWaylandTextureSharingExtension::getExistingFilePath(
const QString &key)
const
224 if (key.contains(QLatin1String(
"../")))
227 for (
auto dir : std::as_const(m_image_dirs)) {
228 QString path = dir + key;
229 if (QFileInfo::exists(path))
233 for (
auto dir : std::as_const(m_image_dirs)) {
234 for (
auto ext : m_image_suffixes) {
235 QString fp = dir + key + ext;
237 if (QFileInfo::exists(fp))
244QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getBuffer(
const QString &key)
246 if (!initServerBufferIntegration())
251 QtWayland::ServerBuffer *buffer =
nullptr;
253 if ((buffer = m_server_buffers.value(key).buffer))
256 QByteArray pixelData;
258 uint glInternalFormat = GL_NONE;
260 if (customPixelData(key, &pixelData, &size, &glInternalFormat)) {
261 if (!pixelData.isEmpty()) {
262 buffer = m_server_buffer_integration->createServerBufferFromData(pixelData, size, glInternalFormat);
264 qWarning() <<
"QWaylandTextureSharingExtension: could not create buffer from custom data for key:" << key;
267 QString pathName = getExistingFilePath(key);
269 if (pathName.isEmpty())
272 buffer = getCompressedBuffer(pathName);
276 QImage img(pathName);
278 img = img.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
279 buffer = m_server_buffer_integration->createServerBufferFromImage(img, QtWayland::ServerBuffer::RGBA32);
285 m_server_buffers.insert(key, BufferInfo(buffer));
293void QWaylandTextureSharingExtension::requestBuffer(
const QString &key)
297 if (thread() != QThread::currentThread())
298 qWarning(
"QWaylandTextureSharingExtension::requestBuffer() called from outside main thread: possible race condition");
300 auto *buffer = getBuffer(key);
303 m_server_buffers[key].usedLocally =
true;
307 emit bufferResult(key, buffer);
310void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_request_image(Resource *resource,
const QString &key)
313 auto *buffer = getBuffer(key);
315 struct ::wl_client *client = resource->client();
316 struct ::wl_resource *buffer_resource = buffer->resourceForClient(client);
319 send_provide_buffer(resource->handle, buffer_resource, key);
321 qWarning() <<
"QWaylandTextureSharingExtension: no buffer resource for client";
323 send_image_failed(resource->handle, key, QString());
328void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_abandon_image(Resource *resource,
const QString &key)
333 QTimer::singleShot(100,
this, &QWaylandTextureSharingExtension::cleanupBuffers);
337void QWaylandTextureSharingExtension::zqt_texture_sharing_v1_destroy_resource(Resource *resource)
342 QTimer::singleShot(1000,
this, &QWaylandTextureSharingExtension::cleanupBuffers);
345bool QWaylandTextureSharingExtension::initServerBufferIntegration()
347 if (!m_server_buffer_integration) {
348 QWaylandCompositor *compositor =
static_cast<QWaylandCompositor *>(extensionContainer());
350 m_server_buffer_integration = QWaylandCompositorPrivate::get(compositor)->serverBufferIntegration();
351 if (!m_server_buffer_integration) {
352 qWarning(
"QWaylandTextureSharingExtension initialization failed: No Server Buffer Integration");
353 if (qEnvironmentVariableIsEmpty(
"QT_WAYLAND_SERVER_BUFFER_INTEGRATION"))
354 qWarning(
"Set the environment variable 'QT_WAYLAND_SERVER_BUFFER_INTEGRATION' to specify.");
361QtWayland::ServerBuffer *QWaylandTextureSharingExtension::getCompressedBuffer(
const QString &pathName)
364 if (!f.open(QIODevice::ReadOnly))
367 QTextureFileReader r(&f, pathName);
372 QTextureFileData td(r.read());
377 qWarning() <<
"VulkanServerBufferIntegration:" << pathName <<
"not valid compressed texture";
381 return m_server_buffer_integration->createServerBufferFromData(td.getDataView(), td.size(),
382 td.glInternalFormat());
385void QWaylandTextureSharingExtension::cleanupBuffers()
387 for (
auto it = m_server_buffers.begin(); it != m_server_buffers.end(); ) {
388 auto *buffer = it.value().buffer;
389 if (!it.value().usedLocally && !buffer->bufferInUse()) {
391 it = m_server_buffers.erase(it);
400void QWaylandTextureSharingExtension::dumpBufferInfo()
402 qDebug() <<
"shared buffers:" << m_server_buffers.size();
403 for (
auto it = m_server_buffers.cbegin(); it != m_server_buffers.cend(); ++it)
404 qDebug() <<
" " << it.key() <<
":" << it.value().buffer <<
"in use" << it.value().buffer->bufferInUse() <<
"usedLocally" << it.value().usedLocally ;
409#include "moc_qwltexturesharingextension_p.cpp"
411#include "qwltexturesharingextension.moc"