Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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#include <QtWaylandCompositor/private/qwltextureorphanage_p.h>
20
21#include <EGL/egl.h>
22#include <EGL/eglext.h>
23#include <unistd.h>
24
25#ifndef GL_TEXTURE_EXTERNAL_OES
26#define GL_TEXTURE_EXTERNAL_OES 0x8D65
27#endif
28
29#ifndef EGL_WAYLAND_BUFFER_WL
30#define EGL_WAYLAND_BUFFER_WL 0x31D5
31#endif
32
33#ifndef EGL_WAYLAND_EGLSTREAM_WL
34#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
35#endif
36
37#ifndef EGL_WAYLAND_PLANE_WL
38#define EGL_WAYLAND_PLANE_WL 0x31D6
39#endif
40
41#ifndef EGL_WAYLAND_Y_INVERTED_WL
42#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
43#endif
44
45#ifndef EGL_TEXTURE_RGB
46#define EGL_TEXTURE_RGB 0x305D
47#endif
48
49#ifndef EGL_TEXTURE_RGBA
50#define EGL_TEXTURE_RGBA 0x305E
51#endif
52
53#ifndef EGL_TEXTURE_EXTERNAL_WL
54#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
55#endif
56
57#ifndef EGL_TEXTURE_Y_U_V_WL
58#define EGL_TEXTURE_Y_U_V_WL 0x31D7
59#endif
60
61#ifndef EGL_TEXTURE_Y_UV_WL
62#define EGL_TEXTURE_Y_UV_WL 0x31D8
63#endif
64
65#ifndef EGL_TEXTURE_Y_XUXV_WL
66#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
67#endif
68
69#ifndef EGL_PLATFORM_X11_KHR
70#define EGL_PLATFORM_X11_KHR 0x31D5
71#endif
72
74
75/* Needed for compatibility with Mesa older than 10.0. */
76typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
77
78#ifndef EGL_WL_bind_wayland_display
79typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
80typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
81#endif
82
83static const char *
84egl_error_string(EGLint code)
85{
86#define MYERRCODE(x) case x: return #x;
87 switch (code) {
88 MYERRCODE(EGL_SUCCESS)
89 MYERRCODE(EGL_NOT_INITIALIZED)
90 MYERRCODE(EGL_BAD_ACCESS)
91 MYERRCODE(EGL_BAD_ALLOC)
92 MYERRCODE(EGL_BAD_ATTRIBUTE)
93 MYERRCODE(EGL_BAD_CONTEXT)
94 MYERRCODE(EGL_BAD_CONFIG)
95 MYERRCODE(EGL_BAD_CURRENT_SURFACE)
96 MYERRCODE(EGL_BAD_DISPLAY)
97 MYERRCODE(EGL_BAD_SURFACE)
98 MYERRCODE(EGL_BAD_MATCH)
99 MYERRCODE(EGL_BAD_PARAMETER)
100 MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
101 MYERRCODE(EGL_BAD_NATIVE_WINDOW)
102 MYERRCODE(EGL_CONTEXT_LOST)
103 default:
104 return "unknown";
105 }
106#undef MYERRCODE
107}
108
109struct BufferState
110{
111 BufferState() = default;
112
114 QOpenGLTexture *textures[3] = {nullptr, nullptr, nullptr};
115 QOpenGLContext *texturesContext[3] = {nullptr, nullptr, nullptr};
118
120
121 bool isYInverted = false;
122 QSize size;
123};
124
126{
127public:
129
130 bool ensureContext();
131 bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle);
134
135 EGLDisplay egl_display = EGL_NO_DISPLAY;
136 bool display_bound = false;
137 ::wl_display *wlDisplay = nullptr;
140
142
143 PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
144 PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
145 PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr;
146
151
152 static bool shuttingDown;
153};
154
156
158{
159 bool localContextNeeded = false;
165 }
166 if (localContext) {
167 if (!offscreenSurface) {
171 }
173 localContextNeeded = true;
174 }
175 }
176 return localContextNeeded;
177}
178
179
181{
182 QMutexLocker locker(&bs->texturesLock);
183
184 bs->textures[plane] = texture;
185 bs->texturesContext[plane] = QOpenGLContext::currentContext();
186
187 Q_ASSERT(bs->texturesContext[plane] != nullptr);
188
189 qCDebug(qLcWaylandCompositorHardwareIntegration)
190 << Q_FUNC_INFO
191 << "(eglstream) creating a cleanup-lambda for QOpenGLContext::aboutToBeDestroyed!"
192 << ", texture: " << bs->textures[plane]
193 << ", ctx: " << (void*)bs->texturesContext[plane];
194
195 bs->texturesAboutToBeDestroyedConnection[plane] =
196 QObject::connect(bs->texturesContext[plane], &QOpenGLContext::aboutToBeDestroyed,
197 bs->texturesContext[plane], [bs, plane]() {
198
199 QMutexLocker locker(&bs->texturesLock);
200
201 // See above lock - there is a chance that this has already been removed from textures[plane]!
202 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
203 if (bs->textures[plane] == nullptr)
204 return;
205
206 delete bs->textures[plane];
207
208 qCDebug(qLcWaylandCompositorHardwareIntegration)
209 << Q_FUNC_INFO
210 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
211 << "Pointer (now dead) was:" << (void*)(bs->textures[plane])
212 << " Associated context (about to die too) is: " << (void*)(bs->texturesContext[plane]);
213
214 bs->textures[plane] = nullptr;
215 bs->texturesContext[plane] = nullptr;
216
217 QObject::disconnect(bs->texturesAboutToBeDestroyedConnection[plane]);
218 bs->texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
219
221}
222
224{
225 BufferState &state = *buffer->d;
227 state.isYInverted = false;
228
230
231 if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
233 close(streamFd);
234 } else {
235 EGLAttrib stream_attribs[] = {
236 EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
237 EGL_NONE
238 };
239 state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
240 }
241
242 if (state.egl_stream == EGL_NO_STREAM_KHR) {
243 qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
244 return false;
245 }
246
247 bool usingLocalContext = ensureContext();
248
250
252 texture->create();
254
255 texture->bind();
256
257 auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
258 if (usingLocalContext)
260
261 if (!newStream) {
262 EGLint code = eglGetError();
263 qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code;
265 state.egl_stream = EGL_NO_STREAM_KHR;
266 return false;
267 }
268 return true;
269}
270
272{
273 bool usingLocalContext = ensureContext();
274
275 BufferState &state = *buffer->d;
276 auto texture = state.textures[0];
277
278 // EGLStream requires calling acquire on every frame.
279 texture->bind();
280 EGLint stream_state;
281 funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
282
283 if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
284 if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
285 qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
286 }
287
288 if (usingLocalContext)
290}
291
292
297
299{
302 if (d->egl_unbind_wayland_display != nullptr && d->display_bound) {
303 Q_ASSERT(d->wlDisplay != nullptr);
304 if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay))
305 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
306 }
307}
308
309void WaylandEglStreamClientBufferIntegration::attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
310{
312 Q_UNUSED(wl_surface);
313
314 auto *clientBuffer = new WaylandEglStreamClientBuffer(this, wl_buffer);
315 auto *bufferManager = QWaylandCompositorPrivate::get(m_compositor)->bufferManager();
316 bufferManager->registerBuffer(wl_buffer, clientBuffer);
317
318 d->initEglStream(clientBuffer, wl_buffer);
319}
320
322{
324
325 const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
326
328 if (!nativeInterface) {
329 qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available.");
330 return;
331 }
332
333 d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay");
334 if (!d->egl_display) {
335 qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window.");
336 return;
337 }
338
339 const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS);
340 if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
341 qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.");
342 return;
343 }
344
345 d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
346 d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
347 if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
348 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
349 return;
350 }
351
352 d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL"));
353 if (!d->egl_query_wayland_buffer) {
354 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.");
355 return;
356 }
357
358 if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
359 d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
360 if (!d->display_bound)
361 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
362
363 d->wlDisplay = display;
364 }
365
366 d->eglStreamController = new WaylandEglStreamController(display, this);
367
368 d->funcs = new QEGLStreamConvenience;
369 d->funcs->initialize(d->egl_display);
370}
371
373{
374 if (wl_shm_buffer_get(buffer))
375 return nullptr;
376
377 return new WaylandEglStreamClientBuffer(this, buffer);
378}
379
380
381WaylandEglStreamClientBuffer::WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration *integration, wl_resource *buffer)
382 : ClientBuffer(buffer)
383 , m_integration(integration)
384{
386 d = new BufferState;
387 if (buffer && !wl_shm_buffer_get(buffer)) {
388 EGLint width, height;
389 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
390 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
391 d->size = QSize(width, height);
392 }
393}
394
396{
398
399 if (p) {
400 if (d->egl_stream)
401 p->funcs->destroy_stream(p->egl_display, d->egl_stream);
402 }
403
404 {
405 QMutexLocker locker(&d->texturesLock);
406
407 for (int i=0; i<3; i++) {
408 if (d->textures[i] != nullptr) {
409
410 qCDebug(qLcWaylandCompositorHardwareIntegration)
411 << Q_FUNC_INFO << " handing over texture!"
412 << (void*)d->textures[i] << "; " << (void*)d->texturesContext[i]
413 << " ... current context might be the same: " << QOpenGLContext::currentContext();
414
416 d->textures[i], d->texturesContext[i]);
417 d->textures[i] = nullptr; // in case the aboutToBeDestroyed lambda is called while we where here
418 d->texturesContext[i] = nullptr;
421 }
422 }
423 }
424
425 delete d;
426}
427
428
433
434
436{
437 return d->size;
438}
439
444
446{
447 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
449
450 if (!m_buffer)
451 return nullptr;
452
453 return d->textures[plane];
454}
455
457{
458 ClientBuffer::setCommitted(damage);
460 p->handleEglstreamTexture(this);
461}
462
void initialize(EGLDisplay dpy)
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()
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
\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.
void bind()
Binds this texture to the currently active texture unit ready for rendering.
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)
Origin
This enum type is used to specify the origin of a QWaylandSurface's buffer.
struct::wl_resource * m_buffer
static QWaylandTextureOrphanage * instance()
bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle)
void setupBufferAndCleanup(BufferState *bs, QOpenGLTexture *texture, int plane)
void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer)
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
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:166
#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]
#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