1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 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 "waylandeglclientbufferintegration.h" |
31 | |
32 | #include <QtWaylandCompositor/QWaylandCompositor> |
33 | #include <qpa/qplatformnativeinterface.h> |
34 | #include <QtGui/QGuiApplication> |
35 | #include <QtGui/QOpenGLContext> |
36 | #include <QtGui/QOpenGLTexture> |
37 | #include <QtGui/QOffscreenSurface> |
38 | #include <qpa/qplatformscreen.h> |
39 | #include <QtGui/QWindow> |
40 | #include <QtCore/QPointer> |
41 | #include <QDebug> |
42 | |
43 | #include <QMutex> |
44 | #include <QMutexLocker> |
45 | #include <QtCore/private/qcore_unix_p.h> |
46 | #include <QtEglSupport/private/qeglstreamconvenience_p.h> |
47 | |
48 | #ifndef GL_TEXTURE_EXTERNAL_OES |
49 | #define GL_TEXTURE_EXTERNAL_OES 0x8D65 |
50 | #endif |
51 | |
52 | #ifndef EGL_WAYLAND_BUFFER_WL |
53 | #define EGL_WAYLAND_BUFFER_WL 0x31D5 |
54 | #endif |
55 | |
56 | #ifndef EGL_WAYLAND_EGLSTREAM_WL |
57 | #define EGL_WAYLAND_EGLSTREAM_WL 0x334B |
58 | #endif |
59 | |
60 | #ifndef EGL_WAYLAND_PLANE_WL |
61 | #define EGL_WAYLAND_PLANE_WL 0x31D6 |
62 | #endif |
63 | |
64 | #ifndef EGL_WAYLAND_Y_INVERTED_WL |
65 | #define EGL_WAYLAND_Y_INVERTED_WL 0x31DB |
66 | #endif |
67 | |
68 | #ifndef EGL_TEXTURE_RGB |
69 | #define EGL_TEXTURE_RGB 0x305D |
70 | #endif |
71 | |
72 | #ifndef EGL_TEXTURE_RGBA |
73 | #define EGL_TEXTURE_RGBA 0x305E |
74 | #endif |
75 | |
76 | #ifndef EGL_TEXTURE_EXTERNAL_WL |
77 | #define EGL_TEXTURE_EXTERNAL_WL 0x31DA |
78 | #endif |
79 | |
80 | #ifndef EGL_TEXTURE_Y_U_V_WL |
81 | #define EGL_TEXTURE_Y_U_V_WL 0x31D7 |
82 | #endif |
83 | |
84 | #ifndef EGL_TEXTURE_Y_UV_WL |
85 | #define EGL_TEXTURE_Y_UV_WL 0x31D8 |
86 | #endif |
87 | |
88 | #ifndef EGL_TEXTURE_Y_XUXV_WL |
89 | #define EGL_TEXTURE_Y_XUXV_WL 0x31D9 |
90 | #endif |
91 | |
92 | #ifndef EGL_PLATFORM_X11_KHR |
93 | #define EGL_PLATFORM_X11_KHR 0x31D5 |
94 | #endif |
95 | |
96 | /* Needed for compatibility with Mesa older than 10.0. */ |
97 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); |
98 | |
99 | #ifndef EGL_WL_bind_wayland_display |
100 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); |
101 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display); |
102 | #endif |
103 | |
104 | #ifndef EGL_KHR_image |
105 | typedef EGLImageKHR (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); |
106 | typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImageKHR image); |
107 | #endif |
108 | |
109 | #ifndef GL_OES_EGL_image |
110 | typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); |
111 | typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image); |
112 | #endif |
113 | |
114 | QT_BEGIN_NAMESPACE |
115 | |
116 | static const char * |
117 | egl_error_string(EGLint code) |
118 | { |
119 | #define MYERRCODE(x) case x: return #x; |
120 | switch (code) { |
121 | MYERRCODE(EGL_SUCCESS) |
122 | MYERRCODE(EGL_NOT_INITIALIZED) |
123 | MYERRCODE(EGL_BAD_ACCESS) |
124 | MYERRCODE(EGL_BAD_ALLOC) |
125 | MYERRCODE(EGL_BAD_ATTRIBUTE) |
126 | MYERRCODE(EGL_BAD_CONTEXT) |
127 | MYERRCODE(EGL_BAD_CONFIG) |
128 | MYERRCODE(EGL_BAD_CURRENT_SURFACE) |
129 | MYERRCODE(EGL_BAD_DISPLAY) |
130 | MYERRCODE(EGL_BAD_SURFACE) |
131 | MYERRCODE(EGL_BAD_MATCH) |
132 | MYERRCODE(EGL_BAD_PARAMETER) |
133 | MYERRCODE(EGL_BAD_NATIVE_PIXMAP) |
134 | MYERRCODE(EGL_BAD_NATIVE_WINDOW) |
135 | MYERRCODE(EGL_CONTEXT_LOST) |
136 | default: |
137 | return "unknown" ; |
138 | } |
139 | #undef MYERRCODE |
140 | } |
141 | |
142 | struct BufferState |
143 | { |
144 | BufferState() = default; |
145 | |
146 | enum EglMode { |
147 | ModeUninitialized, |
148 | ModeEGLImage, |
149 | ModeEGLStream |
150 | }; |
151 | |
152 | EGLint egl_format = EGL_TEXTURE_RGBA; |
153 | QVarLengthArray<EGLImageKHR, 3> egl_images; |
154 | QOpenGLTexture *textures[3] = {}; |
155 | EGLStreamKHR egl_stream = EGL_NO_STREAM_KHR; |
156 | |
157 | bool isYInverted = true; |
158 | QSize size; |
159 | EglMode eglMode = ModeUninitialized; |
160 | }; |
161 | |
162 | class WaylandEglClientBufferIntegrationPrivate |
163 | { |
164 | public: |
165 | WaylandEglClientBufferIntegrationPrivate(); |
166 | |
167 | void initBuffer(WaylandEglClientBuffer *buffer); |
168 | void initEglTexture(WaylandEglClientBuffer *buffer, EGLint format); |
169 | bool ensureContext(); |
170 | bool initEglStream(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle); |
171 | void handleEglstreamTexture(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle); |
172 | void registerBuffer(struct ::wl_resource *buffer, BufferState state); |
173 | void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { orphanedTextures << texture; } |
174 | void deleteOrphanedTextures(); |
175 | |
176 | EGLDisplay egl_display = EGL_NO_DISPLAY; |
177 | bool display_bound = false; |
178 | ::wl_display *wlDisplay = nullptr; |
179 | QOffscreenSurface *offscreenSurface = nullptr; |
180 | QOpenGLContext *localContext = nullptr; |
181 | QVector<QOpenGLTexture *> orphanedTextures; |
182 | |
183 | PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr; |
184 | PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr; |
185 | PFNEGLQUERYWAYLANDBUFFERWL_compat egl_query_wayland_buffer = nullptr; |
186 | |
187 | PFNEGLCREATEIMAGEKHRPROC egl_create_image = nullptr; |
188 | PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image = nullptr; |
189 | |
190 | PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr; |
191 | |
192 | QEGLStreamConvenience *funcs = nullptr; |
193 | static WaylandEglClientBufferIntegrationPrivate *get(WaylandEglClientBufferIntegration *integration) { |
194 | return shuttingDown ? nullptr : integration->d_ptr.data(); |
195 | } |
196 | |
197 | static bool shuttingDown; |
198 | }; |
199 | |
200 | bool WaylandEglClientBufferIntegrationPrivate::shuttingDown = false; |
201 | |
202 | WaylandEglClientBufferIntegrationPrivate::WaylandEglClientBufferIntegrationPrivate() |
203 | { |
204 | } |
205 | |
206 | void WaylandEglClientBufferIntegrationPrivate::initBuffer(WaylandEglClientBuffer *buffer) |
207 | { |
208 | EGLint format; |
209 | |
210 | if (egl_query_wayland_buffer(egl_display, buffer->waylandBufferHandle(), EGL_TEXTURE_FORMAT, &format)) |
211 | initEglTexture(buffer, format); |
212 | } |
213 | |
214 | void WaylandEglClientBufferIntegrationPrivate::initEglTexture(WaylandEglClientBuffer *buffer, EGLint format) |
215 | { |
216 | // Non-streaming case |
217 | |
218 | // Resolving GL functions may need a context current, so do it only here. |
219 | if (!gl_egl_image_target_texture_2d) |
220 | gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES" )); |
221 | |
222 | if (!gl_egl_image_target_texture_2d) { |
223 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
224 | << "BindTextureToBuffer() failed. Could not find glEGLImageTargetTexture2DOES." ; |
225 | return; |
226 | } |
227 | |
228 | BufferState &state = *buffer->d; |
229 | state.egl_format = format; |
230 | state.eglMode = BufferState::ModeEGLImage; |
231 | |
232 | #if defined(EGL_WAYLAND_Y_INVERTED_WL) |
233 | EGLint isYInverted; |
234 | EGLBoolean ret = egl_query_wayland_buffer(egl_display, buffer->waylandBufferHandle(), EGL_WAYLAND_Y_INVERTED_WL, &isYInverted); |
235 | // Yes, this looks strange, but the specification says that EGL_FALSE return |
236 | // value (not supported) should be treated the same as EGL_TRUE return value |
237 | // and EGL_TRUE in value. |
238 | state.isYInverted = (ret == EGL_FALSE || isYInverted == EGL_TRUE); |
239 | #endif |
240 | |
241 | int planes = 1; |
242 | |
243 | switch (format) { |
244 | default: |
245 | case EGL_TEXTURE_RGB: |
246 | case EGL_TEXTURE_RGBA: |
247 | case EGL_TEXTURE_EXTERNAL_WL: |
248 | planes = 1; |
249 | break; |
250 | case EGL_TEXTURE_Y_UV_WL: |
251 | planes = 2; |
252 | break; |
253 | case EGL_TEXTURE_Y_U_V_WL: |
254 | planes = 3; |
255 | break; |
256 | case EGL_TEXTURE_Y_XUXV_WL: |
257 | planes = 2; |
258 | break; |
259 | } |
260 | |
261 | for (int i = 0; i < planes; i++) { |
262 | const EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, i, EGL_NONE }; |
263 | EGLImageKHR image = egl_create_image(egl_display, |
264 | EGL_NO_CONTEXT, |
265 | EGL_WAYLAND_BUFFER_WL, |
266 | buffer->waylandBufferHandle(), |
267 | attribs); |
268 | |
269 | if (image == EGL_NO_IMAGE_KHR) { |
270 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
271 | << "Failed to create EGL image for plane" << i; |
272 | } |
273 | |
274 | state.egl_images << image; |
275 | state.textures[i] = nullptr; |
276 | } |
277 | } |
278 | |
279 | bool WaylandEglClientBufferIntegrationPrivate::ensureContext() |
280 | { |
281 | bool localContextNeeded = false; |
282 | if (!QOpenGLContext::currentContext()) { |
283 | if (!localContext && QOpenGLContext::globalShareContext()) { |
284 | localContext = new QOpenGLContext; |
285 | localContext->setShareContext(QOpenGLContext::globalShareContext()); |
286 | localContext->create(); |
287 | } |
288 | if (localContext) { |
289 | if (!offscreenSurface) { |
290 | offscreenSurface = new QOffscreenSurface; |
291 | offscreenSurface->setFormat(localContext->format()); |
292 | offscreenSurface->create(); |
293 | } |
294 | localContext->makeCurrent(surface: offscreenSurface); |
295 | localContextNeeded = true; |
296 | } |
297 | } |
298 | return localContextNeeded; |
299 | } |
300 | |
301 | bool WaylandEglClientBufferIntegrationPrivate::initEglStream(WaylandEglClientBuffer *buffer, wl_resource *bufferHandle) |
302 | { |
303 | BufferState &state = *buffer->d; |
304 | state.egl_format = EGL_TEXTURE_EXTERNAL_WL; |
305 | state.isYInverted = false; |
306 | |
307 | EGLNativeFileDescriptorKHR streamFd = EGL_NO_FILE_DESCRIPTOR_KHR; |
308 | |
309 | if (egl_query_wayland_buffer(egl_display, bufferHandle, EGL_WAYLAND_BUFFER_WL, &streamFd)) { |
310 | state.egl_stream = funcs->create_stream_from_file_descriptor(egl_display, streamFd); |
311 | close(fd: streamFd); |
312 | } else { |
313 | EGLAttrib stream_attribs[] = { |
314 | EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib)bufferHandle, |
315 | EGL_NONE |
316 | }; |
317 | state.egl_stream = funcs->create_stream_attrib_nv(egl_display, stream_attribs); |
318 | } |
319 | |
320 | if (state.egl_stream == EGL_NO_STREAM_KHR) { |
321 | qCWarning(qLcWaylandCompositorHardwareIntegration, "%s:%d: eglCreateStreamFromFileDescriptorKHR failed: 0x%x" , Q_FUNC_INFO, __LINE__, eglGetError()); |
322 | return false; |
323 | } |
324 | state.eglMode = BufferState::ModeEGLStream; |
325 | |
326 | if (!QOpenGLContext::currentContext()) { |
327 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
328 | << "EglClientBufferIntegration: creating texture with no current context" ; |
329 | return false; |
330 | } |
331 | |
332 | auto texture = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(GL_TEXTURE_EXTERNAL_OES)); |
333 | texture->create(); |
334 | state.textures[0] = texture; // TODO: support multiple planes for the streaming case |
335 | |
336 | texture->bind(); |
337 | |
338 | auto newStream = funcs->stream_consumer_gltexture(egl_display, state.egl_stream); |
339 | if (!newStream) { |
340 | EGLint code = eglGetError(); |
341 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not initialize EGLStream:" << egl_error_string(code) << Qt::hex << (long)code; |
342 | funcs->destroy_stream(egl_display, state.egl_stream); |
343 | state.egl_stream = EGL_NO_STREAM_KHR; |
344 | return false; |
345 | } |
346 | return true; |
347 | } |
348 | |
349 | void WaylandEglClientBufferIntegrationPrivate::handleEglstreamTexture(WaylandEglClientBuffer *buffer, struct ::wl_resource *bufferHandle) |
350 | { |
351 | bool usingLocalContext = ensureContext(); |
352 | |
353 | if (buffer->d->eglMode == BufferState::ModeUninitialized) { |
354 | bool streamOK = initEglStream(buffer, bufferHandle); |
355 | if (!streamOK) |
356 | return; |
357 | } |
358 | |
359 | BufferState &state = *buffer->d; |
360 | auto texture = state.textures[0]; |
361 | |
362 | // EGLStream requires calling acquire on every frame. |
363 | texture->bind(); |
364 | EGLint stream_state; |
365 | funcs->query_stream(egl_display, state.egl_stream, EGL_STREAM_STATE_KHR, &stream_state); |
366 | |
367 | if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) { |
368 | if (funcs->stream_consumer_acquire(egl_display, state.egl_stream) != EGL_TRUE) |
369 | qCWarning(qLcWaylandCompositorHardwareIntegration, |
370 | "%s:%d: eglStreamConsumerAcquireKHR failed: 0x%x" , Q_FUNC_INFO, __LINE__, |
371 | eglGetError()); |
372 | } |
373 | |
374 | if (usingLocalContext) |
375 | localContext->doneCurrent(); |
376 | } |
377 | |
378 | void WaylandEglClientBufferIntegrationPrivate::deleteOrphanedTextures() |
379 | { |
380 | Q_ASSERT(QOpenGLContext::currentContext()); |
381 | qDeleteAll(c: orphanedTextures); |
382 | orphanedTextures.clear(); |
383 | } |
384 | |
385 | WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration() |
386 | : d_ptr(new WaylandEglClientBufferIntegrationPrivate) |
387 | { |
388 | } |
389 | |
390 | WaylandEglClientBufferIntegration::~WaylandEglClientBufferIntegration() |
391 | { |
392 | WaylandEglClientBufferIntegrationPrivate::shuttingDown = true; |
393 | Q_D(WaylandEglClientBufferIntegration); |
394 | if (d->egl_unbind_wayland_display && d->display_bound) { |
395 | Q_ASSERT(d->wlDisplay); |
396 | if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay)) |
397 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed" ; |
398 | } |
399 | } |
400 | |
401 | void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *display) |
402 | { |
403 | Q_D(WaylandEglClientBufferIntegration); |
404 | |
405 | const bool ignoreBindDisplay = !qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY" ).isEmpty(); |
406 | |
407 | QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); |
408 | if (!nativeInterface) { |
409 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
410 | << "Failed to initialize EGL display. No native platform interface available." ; |
411 | return; |
412 | } |
413 | |
414 | d->egl_display = nativeInterface->nativeResourceForIntegration(resource: "EglDisplay" ); |
415 | if (!d->egl_display) { |
416 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
417 | << "Failed to initialize EGL display. Could not get EglDisplay for window." ; |
418 | return; |
419 | } |
420 | |
421 | const char *extensionString = eglQueryString(dpy: d->egl_display, EGL_EXTENSIONS); |
422 | if ((!extensionString || !strstr(haystack: extensionString, needle: "EGL_WL_bind_wayland_display" )) && !ignoreBindDisplay) { |
423 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
424 | << "Failed to initialize EGL display. There is no EGL_WL_bind_wayland_display extension." ; |
425 | return; |
426 | } |
427 | |
428 | d->egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglBindWaylandDisplayWL" )); |
429 | d->egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglUnbindWaylandDisplayWL" )); |
430 | if ((!d->egl_bind_wayland_display || !d->egl_unbind_wayland_display) && !ignoreBindDisplay) { |
431 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
432 | << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL." ; |
433 | return; |
434 | } |
435 | |
436 | d->egl_query_wayland_buffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL_compat>(eglGetProcAddress(procname: "eglQueryWaylandBufferWL" )); |
437 | if (!d->egl_query_wayland_buffer) { |
438 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
439 | << "Failed to initialize EGL display. Could not find eglQueryWaylandBufferWL." ; |
440 | return; |
441 | } |
442 | |
443 | d->egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress(procname: "eglCreateImageKHR" )); |
444 | d->egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress(procname: "eglDestroyImageKHR" )); |
445 | if (!d->egl_create_image || !d->egl_destroy_image) { |
446 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
447 | << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR." ; |
448 | return; |
449 | } |
450 | |
451 | if (d->egl_bind_wayland_display && d->egl_unbind_wayland_display) { |
452 | d->display_bound = d->egl_bind_wayland_display(d->egl_display, display); |
453 | if (!d->display_bound) { |
454 | if (!ignoreBindDisplay) { |
455 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
456 | << "Failed to initialize EGL display. Could not bind Wayland display." ; |
457 | return; |
458 | } else { |
459 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not bind Wayland display. Ignoring." ; |
460 | } |
461 | } |
462 | d->wlDisplay = display; |
463 | } |
464 | |
465 | d->funcs = new QEGLStreamConvenience; |
466 | d->funcs->initialize(dpy: d->egl_display); |
467 | } |
468 | |
469 | QtWayland::ClientBuffer *WaylandEglClientBufferIntegration::createBufferFor(wl_resource *buffer) |
470 | { |
471 | if (wl_shm_buffer_get(resource: buffer)) |
472 | return nullptr; |
473 | return new WaylandEglClientBuffer(this, buffer); |
474 | } |
475 | |
476 | WaylandEglClientBuffer::WaylandEglClientBuffer(WaylandEglClientBufferIntegration *integration, wl_resource *buffer) |
477 | : ClientBuffer(buffer) |
478 | , m_integration(integration) |
479 | { |
480 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
481 | d = new BufferState; |
482 | if (buffer && !wl_shm_buffer_get(resource: buffer)) { |
483 | EGLint width, height; |
484 | p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_WIDTH, &width); |
485 | p->egl_query_wayland_buffer(p->egl_display, buffer, EGL_HEIGHT, &height); |
486 | d->size = QSize(width, height); |
487 | |
488 | p->initBuffer(buffer: this); |
489 | } |
490 | } |
491 | |
492 | |
493 | WaylandEglClientBuffer::~WaylandEglClientBuffer() |
494 | { |
495 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
496 | |
497 | if (p) { |
498 | for (auto image : d->egl_images) |
499 | p->egl_destroy_image(p->egl_display, image); |
500 | |
501 | if (d->egl_stream) |
502 | p->funcs->destroy_stream(p->egl_display, d->egl_stream); |
503 | |
504 | for (auto *texture : d->textures) |
505 | p->deleteGLTextureWhenPossible(texture); |
506 | } |
507 | delete d; |
508 | } |
509 | |
510 | static QWaylandBufferRef::BufferFormatEgl formatFromEglFormat(EGLint format) { |
511 | switch (format) { |
512 | case EGL_TEXTURE_RGB: |
513 | return QWaylandBufferRef::BufferFormatEgl_RGB; |
514 | case EGL_TEXTURE_RGBA: |
515 | return QWaylandBufferRef::BufferFormatEgl_RGBA; |
516 | case EGL_TEXTURE_EXTERNAL_WL: |
517 | return QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES; |
518 | case EGL_TEXTURE_Y_UV_WL: |
519 | return QWaylandBufferRef::BufferFormatEgl_Y_UV; |
520 | case EGL_TEXTURE_Y_U_V_WL: |
521 | return QWaylandBufferRef::BufferFormatEgl_Y_U_V; |
522 | case EGL_TEXTURE_Y_XUXV_WL: |
523 | return QWaylandBufferRef::BufferFormatEgl_Y_XUXV; |
524 | } |
525 | |
526 | return QWaylandBufferRef::BufferFormatEgl_RGBA; |
527 | } |
528 | |
529 | static QOpenGLTexture::TextureFormat openGLFormatFromEglFormat(EGLint format) { |
530 | switch (format) { |
531 | case EGL_TEXTURE_RGB: |
532 | return QOpenGLTexture::RGBFormat; |
533 | case EGL_TEXTURE_RGBA: |
534 | return QOpenGLTexture::RGBAFormat; |
535 | default: |
536 | return QOpenGLTexture::NoFormat; |
537 | } |
538 | } |
539 | |
540 | QWaylandBufferRef::BufferFormatEgl WaylandEglClientBuffer::bufferFormatEgl() const |
541 | { |
542 | return formatFromEglFormat(format: d->egl_format); |
543 | } |
544 | |
545 | QOpenGLTexture *WaylandEglClientBuffer::toOpenGlTexture(int plane) |
546 | { |
547 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
548 | // At this point we should have a valid OpenGL context, so it's safe to destroy textures |
549 | p->deleteOrphanedTextures(); |
550 | |
551 | if (!m_buffer) |
552 | return nullptr; |
553 | |
554 | auto texture = d->textures[plane]; |
555 | if (d->eglMode == BufferState::ModeEGLStream) |
556 | return texture; // EGLStreams texture is maintained by handle_eglstream_texture() |
557 | |
558 | const auto target = static_cast<QOpenGLTexture::Target>(d->egl_format == EGL_TEXTURE_EXTERNAL_WL ? GL_TEXTURE_EXTERNAL_OES |
559 | : GL_TEXTURE_2D); |
560 | if (!texture) { |
561 | texture = new QOpenGLTexture(target); |
562 | texture->setFormat(openGLFormatFromEglFormat(format: d->egl_format)); |
563 | texture->setSize(width: d->size.width(), height: d->size.height()); |
564 | texture->create(); |
565 | d->textures[plane] = texture; |
566 | } |
567 | |
568 | if (m_textureDirty) { |
569 | texture->bind(); |
570 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
571 | p->gl_egl_image_target_texture_2d(target, d->egl_images[plane]); |
572 | } |
573 | return texture; |
574 | } |
575 | |
576 | void WaylandEglClientBuffer::setCommitted(QRegion &damage) |
577 | { |
578 | ClientBuffer::setCommitted(damage); |
579 | if (d->eglMode == BufferState::ModeEGLStream || d->eglMode == BufferState::ModeUninitialized) { |
580 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
581 | p->handleEglstreamTexture(buffer: this, bufferHandle: waylandBufferHandle()); |
582 | } |
583 | } |
584 | |
585 | QWaylandSurface::Origin WaylandEglClientBuffer::origin() const |
586 | { |
587 | return d->isYInverted ? QWaylandSurface::OriginTopLeft : QWaylandSurface::OriginBottomLeft; |
588 | } |
589 | |
590 | quintptr WaylandEglClientBuffer::lockNativeBuffer() |
591 | { |
592 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
593 | |
594 | if (d->egl_stream != EGL_NO_STREAM_KHR) |
595 | return 0; |
596 | |
597 | EGLImageKHR image = p->egl_create_image(p->egl_display, EGL_NO_CONTEXT, |
598 | EGL_WAYLAND_BUFFER_WL, |
599 | m_buffer, nullptr); |
600 | return reinterpret_cast<quintptr>(image); |
601 | } |
602 | |
603 | void WaylandEglClientBuffer::unlockNativeBuffer(quintptr native_buffer) const |
604 | { |
605 | if (!native_buffer) |
606 | return; |
607 | |
608 | auto *p = WaylandEglClientBufferIntegrationPrivate::get(integration: m_integration); |
609 | |
610 | EGLImageKHR image = reinterpret_cast<EGLImageKHR>(native_buffer); |
611 | p->egl_destroy_image(p->egl_display, image); |
612 | } |
613 | |
614 | QSize WaylandEglClientBuffer::size() const |
615 | { |
616 | return d->size; |
617 | } |
618 | |
619 | QT_END_NAMESPACE |
620 | |