Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
waylandeglstreamintegration.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
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtOpenGL/QOpenGLTexture>
9#include <QtGui/QGuiApplication>
10#include <QtGui/QOpenGLContext>
11#include <QtGui/QOffscreenSurface>
12#include <QtCore/QMutexLocker>
13
14#include <QtGui/private/qeglstreamconvenience_p.h>
15#include <qpa/qplatformnativeinterface.h>
16
17#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
18#include <QtWaylandCompositor/private/qwlbuffermanager_p.h>
19
20#include <EGL/egl.h>
21#include <EGL/eglext.h>
22#include <unistd.h>
23
24#ifndef GL_TEXTURE_EXTERNAL_OES
25#define GL_TEXTURE_EXTERNAL_OES 0x8D65
26#endif
27
28#ifndef EGL_WAYLAND_BUFFER_WL
29#define EGL_WAYLAND_BUFFER_WL 0x31D5
30#endif
31
32#ifndef EGL_WAYLAND_EGLSTREAM_WL
33#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
34#endif
35
36#ifndef EGL_WAYLAND_PLANE_WL
37#define EGL_WAYLAND_PLANE_WL 0x31D6
38#endif
39
40#ifndef EGL_WAYLAND_Y_INVERTED_WL
41#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
42#endif
43
44#ifndef EGL_TEXTURE_RGB
45#define EGL_TEXTURE_RGB 0x305D
46#endif
47
48#ifndef EGL_TEXTURE_RGBA
49#define EGL_TEXTURE_RGBA 0x305E
50#endif
51
52#ifndef EGL_TEXTURE_EXTERNAL_WL
53#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
54#endif
55
56#ifndef EGL_TEXTURE_Y_U_V_WL
57#define EGL_TEXTURE_Y_U_V_WL 0x31D7
58#endif
59
60#ifndef EGL_TEXTURE_Y_UV_WL
61#define EGL_TEXTURE_Y_UV_WL 0x31D8
62#endif
63
64#ifndef EGL_TEXTURE_Y_XUXV_WL
65#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
66#endif
67
68#ifndef EGL_PLATFORM_X11_KHR
69#define EGL_PLATFORM_X11_KHR 0x31D5
70#endif
71
73
74/* Needed for compatibility with Mesa older than 10.0. */
75typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
76
77#ifndef EGL_WL_bind_wayland_display
78typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
79typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
80#endif
81
82static const char *
83egl_error_string(EGLint code)
84{
85#define MYERRCODE(x) case x: return #x;
86 switch (code) {
87 MYERRCODE(EGL_SUCCESS)
88 MYERRCODE(EGL_NOT_INITIALIZED)
89 MYERRCODE(EGL_BAD_ACCESS)
90 MYERRCODE(EGL_BAD_ALLOC)
91 MYERRCODE(EGL_BAD_ATTRIBUTE)
92 MYERRCODE(EGL_BAD_CONTEXT)
93 MYERRCODE(EGL_BAD_CONFIG)
94 MYERRCODE(EGL_BAD_CURRENT_SURFACE)
95 MYERRCODE(EGL_BAD_DISPLAY)
96 MYERRCODE(EGL_BAD_SURFACE)
97 MYERRCODE(EGL_BAD_MATCH)
98 MYERRCODE(EGL_BAD_PARAMETER)
99 MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
100 MYERRCODE(EGL_BAD_NATIVE_WINDOW)
101 MYERRCODE(EGL_CONTEXT_LOST)
102 default:
103 return "unknown";
104 }
105#undef MYERRCODE
106}
107
108struct BufferState
109{
110 BufferState() = default;
111
113 QOpenGLTexture *textures[3] = {nullptr, nullptr, nullptr};
114 QOpenGLContext *texturesContext[3] = {nullptr, nullptr, nullptr};
117
119
120 bool isYInverted = false;
121 QSize size;
122};
123
125{
126public:
128
129 bool ensureContext();
130 bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle);
136
137 EGLDisplay egl_display = EGL_NO_DISPLAY;
138 bool display_bound = false;
139 ::wl_display *wlDisplay = nullptr;
142
146
148
149 PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
150 PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
151 PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr;
152
155 return shuttingDown ? nullptr : integration->d_ptr.data();
156 }
157
158 static bool shuttingDown;
159};
160
162
163
165
167
169
172 ctx, [this, texture]() {
173 this->deleteSpecificOrphanedTexture(texture);
175}
176
178{
180
182
183 for (int i=0; i < orphanedTextures.size(); i++) {
184 qCDebug(qLcWaylandCompositorHardwareIntegration)
185 << Q_FUNC_INFO << " about to delete a texture: "
186 << (void*)orphanedTextures[i];
187 }
188
190
193
196}
197
199{
201
203
204 // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function!
205 if (orphanedTextures.length()==0) {
206 qCWarning(qLcWaylandCompositorHardwareIntegration)
207 << Q_FUNC_INFO
208 << "Looks like deleteOrphanedTextures() and this function where called simultaneously!"
209 << "This might cause issues!";
210 return;
211 }
212
214 Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture
215
218
220 delete texture;
221
222 qCDebug(qLcWaylandCompositorHardwareIntegration)
223 << Q_FUNC_INFO
224 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
225 << "Pointer (now dead) was:" << (void*)texture;
226}
227
229{
230 bool localContextNeeded = false;
236 }
237 if (localContext) {
238 if (!offscreenSurface) {
242 }
244 localContextNeeded = true;
245 }
246 }
247 return localContextNeeded;
248}
249
250
252{
253 QMutexLocker locker(&bs->texturesLock);
254
255 bs->textures[plane] = texture;
257
258 Q_ASSERT(bs->texturesContext[plane] != nullptr);
259
260 qCDebug(qLcWaylandCompositorHardwareIntegration)
261 << Q_FUNC_INFO
262 << "(eglstream) creating a cleanup-lambda for QOpenGLContext::aboutToBeDestroyed!"
263 << ", texture: " << bs->textures[plane]
264 << ", ctx: " << (void*)bs->texturesContext[plane];
265
268 bs->texturesContext[plane], [bs, plane]() {
269
270 QMutexLocker locker(&bs->texturesLock);
271
272 // See above lock - there is a chance that this has already been removed from textures[plane]!
273 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
274 if (bs->textures[plane] == nullptr)
275 return;
276
277 delete bs->textures[plane];
278
279 qCDebug(qLcWaylandCompositorHardwareIntegration)
280 << Q_FUNC_INFO
281 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
282 << "Pointer (now dead) was:" << (void*)(bs->textures[plane])
283 << " Associated context (about to die too) is: " << (void*)(bs->texturesContext[plane]);
284
285 bs->textures[plane] = nullptr;
286 bs->texturesContext[plane] = nullptr;
287
288 QObject::disconnect(bs->texturesAboutToBeDestroyedConnection[plane]);
289 bs->texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
290
292}
293
295{
296 BufferState &state = *buffer->d;
297 state.egl_format = EGL_TEXTURE_EXTERNAL_WL;
298 state.isYInverted = false;
299
301
302 if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
304 close(streamFd);
305 } else {
306 EGLAttrib stream_attribs[] = {
307 EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
308 EGL_NONE
309 };
310 state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
311 }
312
313 if (state.egl_stream == EGL_NO_STREAM_KHR) {
314 qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
315 return false;
316 }
317
318 bool usingLocalContext = ensureContext();
319
321
323 texture->create();
325
326 texture->bind();
327
328 auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
329 if (usingLocalContext)
331
332 if (!newStream) {
333 EGLint code = eglGetError();
334 qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code;
336 state.egl_stream = EGL_NO_STREAM_KHR;
337 return false;
338 }
339 return true;
340}
341
343{
344 bool usingLocalContext = ensureContext();
345
346 BufferState &state = *buffer->d;
347 auto texture = state.textures[0];
348
349 // EGLStream requires calling acquire on every frame.
350 texture->bind();
351 EGLint stream_state;
352 funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
353
354 if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
355 if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
356 qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
357 }
358
359 if (usingLocalContext)
361}
362
363
366{
367}
368
370{
373 if (d->egl_unbind_wayland_display != nullptr && d->display_bound) {
374 Q_ASSERT(d->wlDisplay != nullptr);
375 if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay))
376 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
377 }
378}
379
380void WaylandEglStreamClientBufferIntegration::attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
381{
383 Q_UNUSED(wl_surface);
384
385 auto *clientBuffer = new WaylandEglStreamClientBuffer(this, wl_buffer);
387 bufferManager->registerBuffer(wl_buffer, clientBuffer);
388
389 d->initEglStream(clientBuffer, wl_buffer);
390}
391
393{
395
396 const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
397
399 if (!nativeInterface) {
400 qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available.");
401 return;
402 }
403
404 d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay");
405 if (!d->egl_display) {
406 qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window.");
407 return;
408 }
409
410 const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS);
411 if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
412 qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.");
413 return;
414 }
415
416 d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
417 d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
418 if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
419 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
420 return;
421 }
422
423 d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL"));
424 if (!d->egl_query_wayland_buffer) {
425 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.");
426 return;
427 }
428
429 if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
430 d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
431 if (!d->display_bound)
432 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
433
434 d->wlDisplay = display;
435 }
436
437 d->eglStreamController = new WaylandEglStreamController(display, this);
438
439 d->funcs = new QEGLStreamConvenience;
440 d->funcs->initialize(d->egl_display);
441}
442
444{
445 if (wl_shm_buffer_get(buffer))
446 return nullptr;
447
448 return new WaylandEglStreamClientBuffer(this, buffer);
449}
450
451
452WaylandEglStreamClientBuffer::WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration *integration, wl_resource *buffer)
453 : ClientBuffer(buffer)
454 , m_integration(integration)
455{
457 d = new BufferState;
458 if (buffer && !wl_shm_buffer_get(buffer)) {
459 EGLint width, height;
460 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
461 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
462 d->size = QSize(width, height);
463 }
464}
465
467{
469
470 if (p) {
471 if (d->egl_stream)
472 p->funcs->destroy_stream(p->egl_display, d->egl_stream);
473
474 QMutexLocker locker(&d->texturesLock);
475
476 for (int i=0; i<3; i++) {
477 if (d->textures[i] != nullptr) {
478
479 qCDebug(qLcWaylandCompositorHardwareIntegration)
480 << Q_FUNC_INFO << " handing over texture!"
481 << (void*)d->textures[i] << "; " << (void*)d->texturesContext[i]
482 << " ... current context might be the same: " << QOpenGLContext::currentContext();
483
484 p->deleteGLTextureWhenPossible(d->textures[i], d->texturesContext[i]);
485 d->textures[i] = nullptr; // in case the aboutToBeDestroyed lambda is called while we where here
486 d->texturesContext[i] = nullptr;
489 }
490 }
491 }
492
493 delete d;
494}
495
496
498{
500}
501
502
504{
505 return d->size;
506}
507
509{
511}
512
514{
516 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
517 p->deleteOrphanedTextures();
518
519 if (!m_buffer)
520 return nullptr;
521
522 return d->textures[plane];
523}
524
526{
527 ClientBuffer::setCommitted(damage);
529 p->handleEglstreamTexture(this);
530}
531
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
PFNEGLQUERYSTREAMKHRPROC query_stream
PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC create_stream_from_file_descriptor
PFNEGLDESTROYSTREAMKHRPROC destroy_stream
PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC stream_consumer_gltexture
PFNEGLCREATESTREAMATTRIBNVPROC create_stream_attrib_nv
PFNEGLSTREAMCONSUMERACQUIREKHRPROC stream_consumer_acquire
static QPlatformNativeInterface * platformNativeInterface()
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
void removeAt(qsizetype i)
Definition qlist.h:573
T takeAt(qsizetype i)
Definition qlist.h:592
qsizetype length() const noexcept
Definition qlist.h:388
void clear()
Definition qlist.h:417
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
\inmodule QtGui
void create()
Allocates the platform resources associated with the offscreen surface.
void setFormat(const QSurfaceFormat &format)
Sets the offscreen surface format.
\inmodule QtGui
bool create()
Attempts to create the OpenGL context with the current configuration.
bool makeCurrent(QSurface *surface)
Makes the context current in the current thread, against the given surface.
QSurfaceFormat format() const
Returns the format of the underlying platform context, if create() has been called.
void setShareContext(QOpenGLContext *shareContext)
Makes this context share textures, shaders, and other OpenGL resources with shareContext.
void aboutToBeDestroyed()
This signal is emitted before the underlying native OpenGL context is destroyed, such that users may ...
static QOpenGLContext * currentContext()
Returns the last context which called makeCurrent in the current thread, or \nullptr,...
void doneCurrent()
Convenience function for calling makeCurrent with a 0 surface.
static QOpenGLContext * globalShareContext()
\inmodule QtGui
Target
This enum defines the texture target of a QOpenGLTexture object.
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
virtual void * nativeResourceForIntegration(const QByteArray &resource)
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\inmodule QtCore
Definition qsize.h:25
static QWaylandCompositorPrivate * get(QWaylandCompositor *compositor)
QtWayland::BufferManager * bufferManager() const
Origin
This enum type is used to specify the origin of a QWaylandSurface's buffer.
void registerBuffer(struct ::wl_resource *buffer_resource, ClientBuffer *clientBuffer)
struct::wl_resource * m_buffer
void deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx)
bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle)
void setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane)
void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer)
QList< QMetaObject::Connection > orphanedTexturesAboutToBeDestroyedConnection
static WaylandEglStreamClientBufferIntegrationPrivate * get(WaylandEglStreamClientBufferIntegration *integration)
void initializeHardware(struct ::wl_display *display) override
void attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
QtWayland::ClientBuffer * createBufferFor(wl_resource *buffer) override
QOpenGLTexture * toOpenGlTexture(int plane) override
QWaylandSurface::Origin origin() const override
void setCommitted(QRegion &damage) override
QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const override
EGLContext ctx
qDeleteAll(list.begin(), list.end())
else opt state
[0]
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ DirectConnection
#define Q_FUNC_INFO
int EGLNativeFileDescriptorKHR
intptr_t EGLAttrib
#define EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR
typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform
#define EGL_STREAM_STATE_KHR
#define EGL_NO_STREAM_KHR
#define EGL_NO_FILE_DESCRIPTOR_KHR
void * EGLStreamKHR
#define qWarning
Definition qlogging.h:162
#define qCWarning(category,...)
#define qCDebug(category,...)
GLint GLsizei GLsizei height
GLuint const GLuint GLuint const GLuint * textures
GLenum GLuint buffer
GLint GLsizei width
GLenum GLuint texture
GLintptr GLsizeiptr GLeglClientBufferEXT clientBuffer
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
QObject::connect nullptr
BufferState()=default
QMetaObject::Connection texturesAboutToBeDestroyedConnection[3]
QOpenGLContext * texturesContext[3]
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
#define EGL_TEXTURE_EXTERNAL_WL
QT_BEGIN_NAMESPACE typedef struct wl_resource EGLint attribute
static const char * egl_error_string(EGLint code)
QT_BEGIN_NAMESPACE typedef EGLBoolean(EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat)(EGLDisplay dpy
#define EGL_WAYLAND_BUFFER_WL
struct wl_display * display
#define MYERRCODE(x)
QT_BEGIN_NAMESPACE typedef struct wl_resource * buffer
QT_BEGIN_NAMESPACE typedef struct wl_resource EGLint EGLint * value
#define GL_TEXTURE_EXTERNAL_OES
#define EGL_WAYLAND_EGLSTREAM_WL