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
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// Qt-Security score:significant reason:default
4
7
8#include <QtWaylandCompositor/QWaylandCompositor>
9#include <QtOpenGL/QOpenGLTexture>
10#include <QtGui/QGuiApplication>
11#include <QtGui/QOpenGLContext>
12#include <QtGui/QOffscreenSurface>
13#include <QtCore/QMutexLocker>
14
15#include <QtGui/private/qeglstreamconvenience_p.h>
16#include <qpa/qplatformnativeinterface.h>
17
18#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
19#include <QtWaylandCompositor/private/qwlbuffermanager_p.h>
20#include <QtWaylandCompositor/private/qwltextureorphanage_p.h>
21
22#include <EGL/egl.h>
23#include <EGL/eglext.h>
24#include <unistd.h>
25
26#ifndef GL_TEXTURE_EXTERNAL_OES
27#define GL_TEXTURE_EXTERNAL_OES 0x8D65
28#endif
29
31#define EGL_WAYLAND_BUFFER_WL 0x31D5
32#endif
33
34#ifndef EGL_WAYLAND_EGLSTREAM_WL
35#define EGL_WAYLAND_EGLSTREAM_WL 0x334B
36#endif
37
39#define EGL_WAYLAND_PLANE_WL 0x31D6
40#endif
41
43#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
44#endif
45
46#ifndef EGL_TEXTURE_RGB
47#define EGL_TEXTURE_RGB 0x305D
48#endif
49
50#ifndef EGL_TEXTURE_RGBA
51#define EGL_TEXTURE_RGBA 0x305E
52#endif
53
55#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
56#endif
57
59#define EGL_TEXTURE_Y_U_V_WL 0x31D7
60#endif
61
63#define EGL_TEXTURE_Y_UV_WL 0x31D8
64#endif
65
67#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
68#endif
69
71#define EGL_PLATFORM_X11_KHR 0x31D5
72#endif
73
75
76/* Needed for compatibility with Mesa older than 10.0. */
77typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
78
79#ifndef EGL_WL_bind_wayland_display
80typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
81typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
82#endif
83
84static const char *
85egl_error_string(EGLint code)
86{
87#define MYERRCODE(x) case x: return #x;
88 switch (code) {
89 MYERRCODE(EGL_SUCCESS)
90 MYERRCODE(EGL_NOT_INITIALIZED)
91 MYERRCODE(EGL_BAD_ACCESS)
92 MYERRCODE(EGL_BAD_ALLOC)
93 MYERRCODE(EGL_BAD_ATTRIBUTE)
94 MYERRCODE(EGL_BAD_CONTEXT)
95 MYERRCODE(EGL_BAD_CONFIG)
96 MYERRCODE(EGL_BAD_CURRENT_SURFACE)
97 MYERRCODE(EGL_BAD_DISPLAY)
98 MYERRCODE(EGL_BAD_SURFACE)
99 MYERRCODE(EGL_BAD_MATCH)
100 MYERRCODE(EGL_BAD_PARAMETER)
101 MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
102 MYERRCODE(EGL_BAD_NATIVE_WINDOW)
103 MYERRCODE(EGL_CONTEXT_LOST)
104 default:
105 return "unknown";
106 }
107#undef MYERRCODE
108}
109
125
127{
128public:
130
132 bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle);
133 void setupBufferAndCleanup(ControllerBufferState *bs, QOpenGLTexture *texture, int plane);
135
136 EGLDisplay egl_display = EGL_NO_DISPLAY;
137 bool display_bound = false;
138 ::wl_display *wlDisplay = nullptr;
141
143
144 PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
145 PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
147
150 return shuttingDown ? nullptr : integration->d_ptr.data();
151 }
152
153 static bool shuttingDown;
154};
155
157
159{
160 bool localContextNeeded = false;
161 if (!QOpenGLContext::currentContext()) {
162 if (!localContext && QOpenGLContext::globalShareContext()) {
163 localContext = new QOpenGLContext;
164 localContext->setShareContext(QOpenGLContext::globalShareContext());
165 localContext->create();
166 }
167 if (localContext) {
168 if (!offscreenSurface) {
169 offscreenSurface = new QOffscreenSurface;
170 offscreenSurface->setFormat(localContext->format());
171 offscreenSurface->create();
172 }
173 localContext->makeCurrent(offscreenSurface);
174 localContextNeeded = true;
175 }
176 }
177 return localContextNeeded;
178}
179
180
182{
183 QMutexLocker locker(&bs->texturesLock);
184
185 bs->textures[plane] = texture;
186 bs->texturesContext[plane] = QOpenGLContext::currentContext();
187
188 Q_ASSERT(bs->texturesContext[plane] != nullptr);
189
190 qCDebug(qLcWaylandCompositorHardwareIntegration)
191 << Q_FUNC_INFO
192 << "(eglstream) creating a cleanup-lambda for QOpenGLContext::aboutToBeDestroyed!"
193 << ", texture: " << bs->textures[plane]
194 << ", ctx: " << (void*)bs->texturesContext[plane];
195
196 bs->texturesAboutToBeDestroyedConnection[plane] =
197 QObject::connect(bs->texturesContext[plane], &QOpenGLContext::aboutToBeDestroyed,
198 bs->texturesContext[plane], [bs, plane]() {
199
200 QMutexLocker locker(&bs->texturesLock);
201
202 // See above lock - there is a chance that this has already been removed from textures[plane]!
203 // Furthermore, we can trust that all the rest (e.g. disconnect) has also been properly executed!
204 if (bs->textures[plane] == nullptr)
205 return;
206
207 delete bs->textures[plane];
208
209 qCDebug(qLcWaylandCompositorHardwareIntegration)
210 << Q_FUNC_INFO
211 << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!"
212 << "Pointer (now dead) was:" << (void*)(bs->textures[plane])
213 << " Associated context (about to die too) is: " << (void*)(bs->texturesContext[plane]);
214
215 bs->textures[plane] = nullptr;
216 bs->texturesContext[plane] = nullptr;
217
218 QObject::disconnect(bs->texturesAboutToBeDestroyedConnection[plane]);
219 bs->texturesAboutToBeDestroyedConnection[plane] = QMetaObject::Connection();
220
221 }, Qt::DirectConnection);
222}
223
225{
226 ControllerBufferState &state = *buffer->d;
227 state.egl_format = EGL_TEXTURE_EXTERNAL_WL;
228 state.isYInverted = false;
229
230 EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR;
231
232 if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
233 state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd);
234 close(streamFd);
235 } else {
236 EGLAttrib stream_attribs[] = {
237 EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
238 EGL_NONE
239 };
240 state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
241 }
242
243 if (state.egl_stream == EGL_NO_STREAM_KHR) {
244 qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
245 return false;
246 }
247
248 bool usingLocalContext = ensureContext();
249
250 Q_ASSERT(QOpenGLContext::currentContext());
251
252 auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES));
253 texture->create();
254 setupBufferAndCleanup(buffer->d, texture, 0);
255
256 texture->bind();
257
258 auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
259 if (usingLocalContext)
260 localContext->doneCurrent();
261
262 if (!newStream) {
263 EGLint code = eglGetError();
264 qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code;
265 funcs->destroy_stream(egl_display, state.egl_stream);
267 return false;
268 }
269 return true;
270}
271
273{
274 bool usingLocalContext = ensureContext();
275
276 ControllerBufferState &state = *buffer->d;
277 auto texture = state.textures[0];
278
279 // EGLStream requires calling acquire on every frame.
280 texture->bind();
281 EGLint stream_state;
282 funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
283
284 if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
285 if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
286 qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
287 }
288
289 if (usingLocalContext)
290 localContext->doneCurrent();
291}
292
293
294WaylandEglStreamClientBufferIntegration::WaylandEglStreamClientBufferIntegration()
295 : d_ptr(new WaylandEglStreamClientBufferIntegrationPrivate)
296{
297}
298
300{
301 Q_D(WaylandEglStreamClientBufferIntegration);
303 if (d->egl_unbind_wayland_display != nullptr && d->display_bound) {
304 Q_ASSERT(d->wlDisplay != nullptr);
305 if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay))
306 qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed";
307 }
308}
309
310void WaylandEglStreamClientBufferIntegration::attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
311{
312 Q_D(WaylandEglStreamClientBufferIntegration);
313 Q_UNUSED(wl_surface);
314
315 auto *clientBuffer = new WaylandEglStreamClientBuffer(this, wl_buffer);
316 auto *bufferManager = QWaylandCompositorPrivate::get(m_compositor)->bufferManager();
317 bufferManager->registerBuffer(wl_buffer, clientBuffer);
318
319 d->initEglStream(clientBuffer, wl_buffer);
320}
321
323{
324 Q_D(WaylandEglStreamClientBufferIntegration);
325
326 const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
327
328 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
329 if (!nativeInterface) {
330 qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available.");
331 return;
332 }
333
334 d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay");
335 if (!d->egl_display) {
336 qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window.");
337 return;
338 }
339
340 const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS);
341 if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
342 qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.");
343 return;
344 }
345
346 d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
347 d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
348 if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
349 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
350 return;
351 }
352
353 d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL"));
354 if (!d->egl_query_wayland_buffer) {
355 qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.");
356 return;
357 }
358
359 if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
360 d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
361 if (!d->display_bound)
362 qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration.";
363
364 d->wlDisplay = display;
365 }
366
367 d->eglStreamController = new WaylandEglStreamController(display, this);
368
369 d->funcs = new QEGLStreamConvenience;
370 d->funcs->initialize(d->egl_display);
371}
372
374{
375 if (wl_shm_buffer_get(buffer))
376 return nullptr;
377
378 return new WaylandEglStreamClientBuffer(this, buffer);
379}
380
381
382WaylandEglStreamClientBuffer::WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration *integration, wl_resource *buffer)
383 : ClientBuffer(buffer)
384 , m_integration(integration)
385{
387 d = new ControllerBufferState;
388 if (buffer && !wl_shm_buffer_get(buffer)) {
389 EGLint width, height;
390 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
391 p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
392 d->size = QSize(width, height);
393 }
394}
395
397{
399
400 if (p) {
401 if (d->egl_stream)
402 p->funcs->destroy_stream(p->egl_display, d->egl_stream);
403 }
404
405 {
406 QMutexLocker locker(&d->texturesLock);
407
408 for (int i=0; i<3; i++) {
409 if (d->textures[i] != nullptr) {
410
411 qCDebug(qLcWaylandCompositorHardwareIntegration)
412 << Q_FUNC_INFO << " handing over texture!"
413 << (void*)d->textures[i] << "; " << (void*)d->texturesContext[i]
414 << " ... current context might be the same: " << QOpenGLContext::currentContext();
415
416 QtWayland::QWaylandTextureOrphanage::instance()->admitTexture(
417 d->textures[i], d->texturesContext[i]);
418 d->textures[i] = nullptr; // in case the aboutToBeDestroyed lambda is called while we where here
419 d->texturesContext[i] = nullptr;
420 QObject::disconnect(d->texturesAboutToBeDestroyedConnection[i]);
421 d->texturesAboutToBeDestroyedConnection[i] = QMetaObject::Connection();
422 }
423 }
424 }
425
426 delete d;
427}
428
429
431{
432 return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES;
433}
434
435
437{
438 return d->size;
439}
440
442{
443 return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft;
444}
445
447{
448 // At this point we should have a valid OpenGL context, so it's safe to destroy textures
449 QtWayland::QWaylandTextureOrphanage::instance()->deleteTextures();
450
451 if (!m_buffer)
452 return nullptr;
453
454 return d->textures[plane];
455}
456
458{
459 ClientBuffer::setCommitted(damage);
462}
463
464QT_END_NAMESPACE
bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle)
void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer)
void setupBufferAndCleanup(ControllerBufferState *bs, QOpenGLTexture *texture, int plane)
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
#define EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR
#define EGL_STREAM_STATE_KHR
#define EGL_NO_STREAM_KHR
#define EGL_NO_FILE_DESCRIPTOR_KHR
#define EGL_PLATFORM_X11_KHR
#define GL_TEXTURE_EXTERNAL_OES
ControllerBufferState()=default
QMetaObject::Connection texturesAboutToBeDestroyedConnection[3]
#define EGL_TEXTURE_EXTERNAL_WL
#define EGL_WAYLAND_Y_INVERTED_WL
#define MYERRCODE(x)
#define EGL_WAYLAND_EGLSTREAM_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
QT_BEGIN_NAMESPACE typedef struct wl_resource * buffer
QT_BEGIN_NAMESPACE typedef struct wl_resource EGLint EGLint * value