1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "waylandeglstreamintegration.h"
31 #include "waylandeglstreamcontroller.h"
32 
33 #include <QtWaylandCompositor/QWaylandCompositor>
34 #include <QtGui/QGuiApplication>
35 #include <QtGui/QOpenGLContext>
36 #include <QtGui/QOpenGLTexture>
37 #include <QtGui/QOffscreenSurface>
38 
39 #include <QtEglSupport/private/qeglstreamconvenience_p.h>
40 #include <qpa/qplatformnativeinterface.h>
41 
42 #include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
43 #include <QtWaylandCompositor/private/qwlbuffermanager_p.h>
44 
45 #include <EGL/egl.h>
46 #include <EGL/eglext.h>
47 #include <unistd.h>
48 
49 #ifndef GL_TEXTURE_EXTERNAL_OES
50 #define GL_TEXTURE_EXTERNAL_OES     0x8D65
51 #endif
52 
53 #ifndef EGL_WAYLAND_BUFFER_WL
54 #define EGL_WAYLAND_BUFFER_WL       0x31D5
55 #endif
56 
57 #ifndef EGL_WAYLAND_EGLSTREAM_WL
58 #define EGL_WAYLAND_EGLSTREAM_WL    0x334B
59 #endif
60 
61 #ifndef EGL_WAYLAND_PLANE_WL
62 #define EGL_WAYLAND_PLANE_WL        0x31D6
63 #endif
64 
65 #ifndef EGL_WAYLAND_Y_INVERTED_WL
66 #define EGL_WAYLAND_Y_INVERTED_WL   0x31DB
67 #endif
68 
69 #ifndef EGL_TEXTURE_RGB
70 #define EGL_TEXTURE_RGB             0x305D
71 #endif
72 
73 #ifndef EGL_TEXTURE_RGBA
74 #define EGL_TEXTURE_RGBA            0x305E
75 #endif
76 
77 #ifndef EGL_TEXTURE_EXTERNAL_WL
78 #define EGL_TEXTURE_EXTERNAL_WL     0x31DA
79 #endif
80 
81 #ifndef EGL_TEXTURE_Y_U_V_WL
82 #define EGL_TEXTURE_Y_U_V_WL        0x31D7
83 #endif
84 
85 #ifndef EGL_TEXTURE_Y_UV_WL
86 #define EGL_TEXTURE_Y_UV_WL         0x31D8
87 #endif
88 
89 #ifndef EGL_TEXTURE_Y_XUXV_WL
90 #define EGL_TEXTURE_Y_XUXV_WL       0x31D9
91 #endif
92 
93 #ifndef EGL_PLATFORM_X11_KHR
94 #define EGL_PLATFORM_X11_KHR        0x31D5
95 #endif
96 
97 QT_BEGIN_NAMESPACE
98 
99 /* Needed for compatibility with Mesa older than 10.0. */
100 typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
101 
102 #ifndef EGL_WL_bind_wayland_display
103 typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
104 typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
105 #endif
106 
107 static const char *
egl_error_string(EGLint code)108 egl_error_string(EGLint code)
109 {
110 #define MYERRCODE(x) case x: return #x;
111     switch (code) {
112     MYERRCODE(EGL_SUCCESS)
113     MYERRCODE(EGL_NOT_INITIALIZED)
114     MYERRCODE(EGL_BAD_ACCESS)
115     MYERRCODE(EGL_BAD_ALLOC)
116     MYERRCODE(EGL_BAD_ATTRIBUTE)
117     MYERRCODE(EGL_BAD_CONTEXT)
118     MYERRCODE(EGL_BAD_CONFIG)
119     MYERRCODE(EGL_BAD_CURRENT_SURFACE)
120     MYERRCODE(EGL_BAD_DISPLAY)
121     MYERRCODE(EGL_BAD_SURFACE)
122     MYERRCODE(EGL_BAD_MATCH)
123     MYERRCODE(EGL_BAD_PARAMETER)
124     MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
125     MYERRCODE(EGL_BAD_NATIVE_WINDOW)
126     MYERRCODE(EGL_CONTEXT_LOST)
127     default:
128         return "unknown";
129     }
130 #undef MYERRCODE
131 }
132 
133 struct BufferState
134 {
135     BufferState() = default;
136 
137     EGLint egl_format = EGL_TEXTURE_EXTERNAL_WL;
138     QOpenGLTexture *textures[3] = {};
139     EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR;
140 
141     bool isYInverted = false;
142     QSize size;
143 };
144 
145 class WaylandEglStreamClientBufferIntegrationPrivate
146 {
147 public:
148     WaylandEglStreamClientBufferIntegrationPrivate() = default;
149 
150     bool ensureContext();
151     bool initEglStream(WaylandEglStreamClientBuffer *buffer, struct ::wl_resource *bufferHandle);
152     void handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer);
deleteGLTextureWhenPossible(QOpenGLTexture * texture)153     void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; }
154     void deleteOrphanedTextures();
155 
156     EGLDisplay egl_display = EGL_NO_DISPLAY;
157     bool display_bound = false;
158     QOffscreenSurface *offscreenSurface = nullptr;
159     QOpenGLContext *localContext = nullptr;
160     QVector<QOpenGLTexture *> orphanedTextures;
161 
162     WaylandEglStreamController *eglStreamController = nullptr;
163 
164     PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr;
165     PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr;
166     PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr;
167 
168     QEGLStreamConvenience *funcs = nullptr;
get(WaylandEglStreamClientBufferIntegration * integration)169     static WaylandEglStreamClientBufferIntegrationPrivate *get(WaylandEglStreamClientBufferIntegration *integration) {
170         return shuttingDown ? nullptr : integration->d_ptr.data();
171     }
172 
173     static bool shuttingDown;
174 };
175 
176 bool WaylandEglStreamClientBufferIntegrationPrivate::shuttingDown = false;
177 
deleteOrphanedTextures()178 void WaylandEglStreamClientBufferIntegrationPrivate::deleteOrphanedTextures()
179 {
180     Q_ASSERT(QOpenGLContext::currentContext());
181     qDeleteAll(orphanedTextures);
182     orphanedTextures.clear();
183 }
184 
ensureContext()185 bool WaylandEglStreamClientBufferIntegrationPrivate::ensureContext()
186 {
187     bool localContextNeeded = false;
188     if (!QOpenGLContext::currentContext()) {
189         if (!localContext && QOpenGLContext::globalShareContext()) {
190             localContext = new QOpenGLContext;
191             localContext->setShareContext(QOpenGLContext::globalShareContext());
192             localContext->create();
193         }
194         if (localContext) {
195             if (!offscreenSurface) {
196                 offscreenSurface = new QOffscreenSurface;
197                 offscreenSurface->setFormat(localContext->format());
198                 offscreenSurface->create();
199             }
200             localContext->makeCurrent(offscreenSurface);
201             localContextNeeded = true;
202         }
203     }
204     return localContextNeeded;
205 }
206 
207 
initEglStream(WaylandEglStreamClientBuffer * buffer,wl_resource * bufferHandle)208 bool WaylandEglStreamClientBufferIntegrationPrivate::initEglStream(WaylandEglStreamClientBuffer *buffer, wl_resource *bufferHandle)
209 {
210     BufferState &state = *buffer->d;
211     state.egl_format = EGL_TEXTURE_EXTERNAL_WL;
212     state.isYInverted = false;
213 
214     EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR;
215 
216     if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) {
217         state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd);
218         close(streamFd);
219     } else {
220         EGLAttrib stream_attribs[] = {
221             EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle,
222             EGL_NONE
223         };
224         state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs);
225     }
226 
227     if (state.egl_stream == EGL_NO_STREAM_KHR) {
228         qWarning("%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
229         return false;
230     }
231 
232     bool usingLocalContext = ensureContext();
233 
234     Q_ASSERT(QOpenGLContext::currentContext());
235 
236     auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES));
237     texture->create();
238     state.textures[0] = texture; // TODO: support multiple planes
239 
240     texture->bind();
241 
242     auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream);
243     if (usingLocalContext)
244         localContext->doneCurrent();
245 
246     if (!newStream) {
247         EGLint code = eglGetError();
248         qWarning() << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code;
249         funcs->destroy_stream(egl_display, state.egl_stream);
250         state.egl_stream = EGL_NO_STREAM_KHR;
251         return false;
252     }
253     return true;
254 }
255 
handleEglstreamTexture(WaylandEglStreamClientBuffer * buffer)256 void WaylandEglStreamClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEglStreamClientBuffer *buffer)
257 {
258     bool usingLocalContext = ensureContext();
259 
260     BufferState &state = *buffer->d;
261     auto texture = state.textures[0];
262 
263     // EGLStream requires calling acquire on every frame.
264     texture->bind();
265     EGLint stream_state;
266     funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state);
267 
268     if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
269         if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE)
270             qWarning("%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x", Q_FUNC_INFO, __LINE__, eglGetError());
271     }
272 
273     if (usingLocalContext)
274         localContext->doneCurrent();
275 }
276 
277 
WaylandEglStreamClientBufferIntegration()278 WaylandEglStreamClientBufferIntegration::WaylandEglStreamClientBufferIntegration()
279     : d_ptr(new WaylandEglStreamClientBufferIntegrationPrivate)
280 {
281 }
282 
~WaylandEglStreamClientBufferIntegration()283 WaylandEglStreamClientBufferIntegration::~WaylandEglStreamClientBufferIntegration()
284 {
285     WaylandEglStreamClientBufferIntegrationPrivate::shuttingDown = true;
286 }
287 
attachEglStreamConsumer(struct::wl_resource * wl_surface,struct::wl_resource * wl_buffer)288 void WaylandEglStreamClientBufferIntegration::attachEglStreamConsumer(struct ::wl_resource *wl_surface, struct ::wl_resource *wl_buffer)
289 {
290     Q_D(WaylandEglStreamClientBufferIntegration);
291     Q_UNUSED(wl_surface);
292 
293     // NOTE: must use getBuffer to create the buffer here, so the buffer will end up in the buffer manager's hash
294 
295     auto *bufferManager = QWaylandCompositorPrivate::get(m_compositor)->bufferManager();
296     auto *clientBuffer = static_cast<WaylandEglStreamClientBuffer*>(bufferManager->getBuffer(wl_buffer));
297 
298     d->initEglStream(clientBuffer, wl_buffer);
299 }
300 
initializeHardware(struct wl_display * display)301 void WaylandEglStreamClientBufferIntegration::initializeHardware(struct wl_display *display)
302 {
303     Q_D(WaylandEglStreamClientBufferIntegration);
304 
305     const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty();
306 
307     QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
308     if (!nativeInterface) {
309         qWarning("QtCompositor: Failed to initialize EGL display. No native platform interface available.");
310         return;
311     }
312 
313     d->egl_display = nativeInterface->nativeResourceForIntegration("EglDisplay");
314     if (!d->egl_display) {
315         qWarning("QtCompositor: Failed to initialize EGL display. Could not get EglDisplay for window.");
316         return;
317     }
318 
319     const char *extensionString = eglQueryString(d->egl_display, EGL_EXTENSIONS);
320     if ((!extensionString || !strstr(extensionString, "EGL_WL_bind_wayland_display")) && !ignoreBindDisplay) {
321         qWarning("QtCompositor: Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension.");
322         return;
323     }
324 
325     d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"));
326     d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL"));
327     if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) {
328         qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL.");
329         return;
330     }
331 
332     d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress("eglQueryWaylandBufferWL"));
333     if (!d->egl_query_wayland_buffer) {
334         qWarning("QtCompositor: Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL.");
335         return;
336     }
337 
338     if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) {
339         d->display_bound = d->egl_bind_wayland_display(d->egl_display, display);
340         if (!d->display_bound) {
341             if (!ignoreBindDisplay) {
342                 qWarning("QtCompositor: Failed to initialize EGL display. Could not bind Wayland display.");
343                 return;
344             } else {
345                 qWarning("QtCompositor: Could not bind Wayland display. Ignoring.");
346             }
347         }
348     }
349 
350     d->eglStreamController = new WaylandEglStreamController(display, this);
351 
352     d->funcs = new QEGLStreamConvenience;
353     d->funcs->initialize(d->egl_display);
354 }
355 
createBufferFor(wl_resource * buffer)356 QtWayland::ClientBuffer *WaylandEglStreamClientBufferIntegration::createBufferFor(wl_resource *buffer)
357 {
358     if (wl_shm_buffer_get(buffer))
359         return nullptr;
360 
361     return new WaylandEglStreamClientBuffer(this, buffer);
362 }
363 
364 
WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration * integration,wl_resource * buffer)365 WaylandEglStreamClientBuffer::WaylandEglStreamClientBuffer(WaylandEglStreamClientBufferIntegration *integration, wl_resource *buffer)
366     : ClientBuffer(buffer)
367     , m_integration(integration)
368 {
369     auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
370     d = new BufferState;
371     if (buffer && !wl_shm_buffer_get(buffer)) {
372         EGLint width, height;
373         p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width);
374         p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height);
375         d->size = QSize(width, height);
376     }
377 }
378 
~WaylandEglStreamClientBuffer()379 WaylandEglStreamClientBuffer::~WaylandEglStreamClientBuffer()
380 {
381     auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
382 
383     if (p) {
384         if (d->egl_stream)
385             p->funcs->destroy_stream(p->egl_display, d->egl_stream);
386 
387         for (auto *texture : d->textures)
388             p->deleteGLTextureWhenPossible(texture);
389     }
390     delete d;
391 }
392 
393 
bufferFormatEgl() const394 QWaylandBufferRef::BufferFormatEgl WaylandEglStreamClientBuffer::bufferFormatEgl() const
395 {
396     return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES;
397 }
398 
399 
size() const400 QSize WaylandEglStreamClientBuffer::size() const
401 {
402     return d->size;
403 }
404 
origin() const405 QWaylandSurface::Origin WaylandEglStreamClientBuffer::origin() const
406 {
407     return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft;
408 }
409 
toOpenGlTexture(int plane)410 QOpenGLTexture *WaylandEglStreamClientBuffer::toOpenGlTexture(int plane)
411 {
412     auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
413     // At this point we should have a valid OpenGL context, so it's safe to destroy textures
414     p->deleteOrphanedTextures();
415 
416     if (!m_buffer)
417         return nullptr;
418 
419     return d->textures[plane];
420 }
421 
setCommitted(QRegion & damage)422 void WaylandEglStreamClientBuffer::setCommitted(QRegion &damage)
423 {
424     ClientBuffer::setCommitted(damage);
425     auto *p = WaylandEglStreamClientBufferIntegrationPrivate::get(m_integration);
426     p->handleEglstreamTexture(this);
427 }
428 
429 QT_END_NAMESPACE
430