1 | // Copyright (C) 2018 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "linuxdmabufclientbufferintegration.h" |
5 | #include "linuxdmabuf.h" |
6 | |
7 | #include <QtWaylandCompositor/QWaylandCompositor> |
8 | #include <QtWaylandCompositor/private/qwayland-server-wayland.h> |
9 | #include <qpa/qplatformnativeinterface.h> |
10 | #include <QtOpenGL/QOpenGLTexture> |
11 | #include <QtCore/QVarLengthArray> |
12 | #include <QtGui/QGuiApplication> |
13 | #include <QtGui/QOpenGLContext> |
14 | |
15 | #include <EGL/egl.h> |
16 | #include <EGL/eglext.h> |
17 | #include <unistd.h> |
18 | #include <drm_fourcc.h> |
19 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | static QWaylandBufferRef::BufferFormatEgl formatFromDrmFormat(EGLint format) { |
23 | switch (format) { |
24 | case DRM_FORMAT_RGB332: |
25 | case DRM_FORMAT_BGR233: |
26 | case DRM_FORMAT_XRGB4444: |
27 | case DRM_FORMAT_XBGR4444: |
28 | case DRM_FORMAT_RGBX4444: |
29 | case DRM_FORMAT_BGRX4444: |
30 | case DRM_FORMAT_XRGB1555: |
31 | case DRM_FORMAT_XBGR1555: |
32 | case DRM_FORMAT_RGBX5551: |
33 | case DRM_FORMAT_BGRX5551: |
34 | case DRM_FORMAT_RGB565: |
35 | case DRM_FORMAT_BGR565: |
36 | case DRM_FORMAT_RGB888: |
37 | case DRM_FORMAT_BGR888: |
38 | case DRM_FORMAT_XRGB8888: |
39 | case DRM_FORMAT_XBGR8888: |
40 | case DRM_FORMAT_RGBX8888: |
41 | case DRM_FORMAT_BGRX8888: |
42 | case DRM_FORMAT_XRGB2101010: |
43 | case DRM_FORMAT_XBGR2101010: |
44 | case DRM_FORMAT_RGBX1010102: |
45 | case DRM_FORMAT_BGRX1010102: |
46 | return QWaylandBufferRef::BufferFormatEgl_RGB; |
47 | case DRM_FORMAT_ARGB4444: |
48 | case DRM_FORMAT_ABGR4444: |
49 | case DRM_FORMAT_RGBA4444: |
50 | case DRM_FORMAT_BGRA4444: |
51 | case DRM_FORMAT_ARGB1555: |
52 | case DRM_FORMAT_ABGR1555: |
53 | case DRM_FORMAT_RGBA5551: |
54 | case DRM_FORMAT_BGRA5551: |
55 | case DRM_FORMAT_ARGB8888: |
56 | case DRM_FORMAT_ABGR8888: |
57 | case DRM_FORMAT_RGBA8888: |
58 | case DRM_FORMAT_BGRA8888: |
59 | case DRM_FORMAT_ARGB2101010: |
60 | case DRM_FORMAT_ABGR2101010: |
61 | case DRM_FORMAT_RGBA1010102: |
62 | case DRM_FORMAT_BGRA1010102: |
63 | return QWaylandBufferRef::BufferFormatEgl_RGBA; |
64 | case DRM_FORMAT_YUYV: |
65 | return QWaylandBufferRef::BufferFormatEgl_Y_XUXV; |
66 | default: |
67 | qCDebug(qLcWaylandCompositorHardwareIntegration) << "Buffer format" << Qt::hex << format << "not supported" ; |
68 | return QWaylandBufferRef::BufferFormatEgl_Null; |
69 | } |
70 | } |
71 | |
72 | static QOpenGLTexture::TextureFormat openGLFormatFromBufferFormat(QWaylandBufferRef::BufferFormatEgl format) { |
73 | switch (format) { |
74 | case QWaylandBufferRef::BufferFormatEgl_RGB: |
75 | return QOpenGLTexture::RGBFormat; |
76 | case QWaylandBufferRef::BufferFormatEgl_RGBA: |
77 | return QOpenGLTexture::RGBAFormat; |
78 | default: |
79 | return QOpenGLTexture::NoFormat; |
80 | } |
81 | } |
82 | |
83 | // Initialize the EGLImage for a dmabuf buffer which conceptually consists of a |
84 | // single plane. Note that depending on the modifiers, the buffer may be actually |
85 | // transported as multiple dmabuf planes which must be combined into a single |
86 | // EGLImage. For formats where the buffer needs to be represented as multiple |
87 | // EGLImages (e.g., various YUV formats) a different approach is required. |
88 | bool LinuxDmabufClientBufferIntegration::initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer) |
89 | { |
90 | bool success = true; |
91 | |
92 | // Resolving GL functions may need a context current, so do it only here. |
93 | if (!gl_egl_image_target_texture_2d) |
94 | gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES" )); |
95 | |
96 | if (dmabufBuffer->plane(index: 0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) { |
97 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported." ; |
98 | success = false; |
99 | } |
100 | |
101 | // 6 entries for the common attribs plus 10 per possible plane, plus 1 for |
102 | // the final EGL_NONE sentinel. |
103 | QVarLengthArray<EGLint, 6 + 10 * 4 + 1> attribs; |
104 | |
105 | attribs.append(EGL_WIDTH); |
106 | attribs.append(t: dmabufBuffer->size().width()); |
107 | attribs.append(EGL_HEIGHT); |
108 | attribs.append(t: dmabufBuffer->size().height()); |
109 | attribs.append(EGL_LINUX_DRM_FOURCC_EXT); |
110 | attribs.append(t: EGLint(dmabufBuffer->drmFormat())); |
111 | |
112 | #define ADD_PLANE_ATTRIBS(plane_idx) { \ |
113 | attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT); \ |
114 | attribs.append(dmabufBuffer->plane(plane_idx).fd); \ |
115 | attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT); \ |
116 | attribs.append(EGLint(dmabufBuffer->plane(plane_idx).offset)); \ |
117 | attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT); \ |
118 | attribs.append(EGLint(dmabufBuffer->plane(plane_idx).stride)); \ |
119 | if (dmabufBuffer->plane(plane_idx).modifiers != DRM_FORMAT_MOD_INVALID) { \ |
120 | attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT); \ |
121 | attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers & 0xffffffff)); \ |
122 | attribs.append(EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT); \ |
123 | attribs.append(EGLint(dmabufBuffer->plane(plane_idx).modifiers >> 32)); \ |
124 | } \ |
125 | } |
126 | |
127 | switch (dmabufBuffer->planesNumber()) { |
128 | case 4: |
129 | ADD_PLANE_ATTRIBS(3); |
130 | Q_FALLTHROUGH(); |
131 | case 3: |
132 | ADD_PLANE_ATTRIBS(2); |
133 | Q_FALLTHROUGH(); |
134 | case 2: |
135 | ADD_PLANE_ATTRIBS(1); |
136 | Q_FALLTHROUGH(); |
137 | case 1: |
138 | ADD_PLANE_ATTRIBS(0); |
139 | break; |
140 | default: |
141 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses invalid number of planes:" << dmabufBuffer->planesNumber(); |
142 | return false; |
143 | } |
144 | |
145 | attribs.append(EGL_NONE); |
146 | |
147 | // note: EGLImageKHR does NOT take ownership of the file descriptors |
148 | EGLImageKHR image = egl_create_image(m_eglDisplay, |
149 | EGL_NO_CONTEXT, |
150 | EGL_LINUX_DMA_BUF_EXT, |
151 | (EGLClientBuffer) nullptr, |
152 | attribs.constData()); |
153 | |
154 | if (image == EGL_NO_IMAGE_KHR) { |
155 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image from" << |
156 | dmabufBuffer->planesNumber() << "plane(s)" ; |
157 | success = false; |
158 | } |
159 | |
160 | dmabufBuffer->initImage(plane: 0, image); |
161 | |
162 | return success; |
163 | } |
164 | |
165 | bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer) |
166 | { |
167 | bool success = true; |
168 | |
169 | const YuvFormatConversion conversion = m_yuvFormats.value(key: dmabufBuffer->drmFormat()); |
170 | if (conversion.inputPlanes != dmabufBuffer->planesNumber()) { |
171 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer for this format must provide" << conversion.inputPlanes |
172 | << "planes but only" << dmabufBuffer->planesNumber() << "received" ; |
173 | return false; |
174 | } |
175 | |
176 | // Resolving GL functions may need a context current, so do it only here. |
177 | if (!gl_egl_image_target_texture_2d) |
178 | gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress(procname: "glEGLImageTargetTexture2DOES" )); |
179 | |
180 | |
181 | if (dmabufBuffer->plane(index: 0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) { |
182 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported." ; |
183 | success = false; |
184 | } |
185 | |
186 | for (uint32_t i = 0; i < conversion.outputPlanes; ++i) { |
187 | const YuvPlaneConversion plane = conversion.plane[i]; |
188 | |
189 | QVarLengthArray<EGLint, 17> attribs = { |
190 | EGL_WIDTH, dmabufBuffer->size().width() / plane.widthDivisor, |
191 | EGL_HEIGHT, dmabufBuffer->size().height() / plane.heightDivisor, |
192 | EGL_LINUX_DRM_FOURCC_EXT, plane.format, |
193 | EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(index: plane.planeIndex).fd, |
194 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).offset), |
195 | EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).stride), |
196 | EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).modifiers & 0xffffffff), |
197 | EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(index: plane.planeIndex).modifiers >> 32), |
198 | EGL_NONE |
199 | }; |
200 | |
201 | // note: EGLImageKHR does NOT take ownership of the file descriptors |
202 | EGLImageKHR image = egl_create_image(m_eglDisplay, |
203 | EGL_NO_CONTEXT, |
204 | EGL_LINUX_DMA_BUF_EXT, |
205 | (EGLClientBuffer) nullptr, |
206 | attribs.constData()); |
207 | |
208 | if (image == EGL_NO_IMAGE_KHR) { |
209 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i; |
210 | success = false; |
211 | } |
212 | |
213 | dmabufBuffer->initImage(plane: i, image); |
214 | } |
215 | return success; |
216 | } |
217 | |
218 | LinuxDmabufClientBufferIntegration::LinuxDmabufClientBufferIntegration() |
219 | { |
220 | YuvPlaneConversion firstPlane; |
221 | firstPlane.format = DRM_FORMAT_GR88; |
222 | firstPlane.widthDivisor = 1; |
223 | firstPlane.heightDivisor = 1; |
224 | firstPlane.planeIndex = 0; |
225 | |
226 | YuvPlaneConversion secondPlane; |
227 | secondPlane.format = DRM_FORMAT_ARGB8888; |
228 | secondPlane.widthDivisor = 2; |
229 | secondPlane.heightDivisor = 1; |
230 | secondPlane.planeIndex = 0; |
231 | |
232 | YuvFormatConversion formatConversion; |
233 | formatConversion.inputPlanes = 1; |
234 | formatConversion.outputPlanes = 2; |
235 | formatConversion.plane[0] = firstPlane; |
236 | formatConversion.plane[1] = secondPlane; |
237 | |
238 | m_yuvFormats.insert(DRM_FORMAT_YUYV, value: formatConversion); |
239 | } |
240 | |
241 | LinuxDmabufClientBufferIntegration::~LinuxDmabufClientBufferIntegration() |
242 | { |
243 | m_importedBuffers.clear(); |
244 | |
245 | if (egl_unbind_wayland_display != nullptr && m_displayBound) { |
246 | Q_ASSERT(m_wlDisplay != nullptr); |
247 | if (!egl_unbind_wayland_display(m_eglDisplay, m_wlDisplay)) |
248 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "eglUnbindWaylandDisplayWL failed" ; |
249 | } |
250 | } |
251 | |
252 | void LinuxDmabufClientBufferIntegration::initializeHardware(struct ::wl_display *display) |
253 | { |
254 | m_linuxDmabuf.reset(other: new LinuxDmabuf(display, this)); |
255 | |
256 | const bool ignoreBindDisplay = !qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY" ).isEmpty() && qgetenv(varName: "QT_WAYLAND_IGNORE_BIND_DISPLAY" ).toInt() != 0; |
257 | |
258 | // initialize hardware extensions |
259 | egl_query_dmabuf_modifiers_ext = reinterpret_cast<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(eglGetProcAddress(procname: "eglQueryDmaBufModifiersEXT" )); |
260 | egl_query_dmabuf_formats_ext = reinterpret_cast<PFNEGLQUERYDMABUFFORMATSEXTPROC>(eglGetProcAddress(procname: "eglQueryDmaBufFormatsEXT" )); |
261 | if (!egl_query_dmabuf_modifiers_ext || !egl_query_dmabuf_formats_ext) { |
262 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglQueryDmaBufModifiersEXT and eglQueryDmaBufFormatsEXT." ; |
263 | return; |
264 | } |
265 | |
266 | egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglBindWaylandDisplayWL" )); |
267 | egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress(procname: "eglUnbindWaylandDisplayWL" )); |
268 | if ((!egl_bind_wayland_display || !egl_unbind_wayland_display) && !ignoreBindDisplay) { |
269 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL." ; |
270 | return; |
271 | } |
272 | |
273 | egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress(procname: "eglCreateImageKHR" )); |
274 | egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress(procname: "eglDestroyImageKHR" )); |
275 | if (!egl_create_image || !egl_destroy_image) { |
276 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR." ; |
277 | return; |
278 | } |
279 | |
280 | // initialize EGL display |
281 | QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); |
282 | if (!nativeInterface) { |
283 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. No native platform interface available." ; |
284 | return; |
285 | } |
286 | |
287 | m_eglDisplay = nativeInterface->nativeResourceForIntegration(resource: "EglDisplay" ); |
288 | if (!m_eglDisplay) { |
289 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not get EglDisplay for window." ; |
290 | return; |
291 | } |
292 | |
293 | const char *extensionString = eglQueryString(dpy: m_eglDisplay, EGL_EXTENSIONS); |
294 | if (!extensionString || !strstr(haystack: extensionString, needle: "EGL_EXT_image_dma_buf_import" )) { |
295 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. There is no EGL_EXT_image_dma_buf_import extension." ; |
296 | return; |
297 | } |
298 | if (strstr(haystack: extensionString, needle: "EGL_EXT_image_dma_buf_import_modifiers" )) |
299 | m_supportsDmabufModifiers = true; |
300 | |
301 | if (egl_bind_wayland_display && egl_unbind_wayland_display) { |
302 | m_displayBound = egl_bind_wayland_display(m_eglDisplay, display); |
303 | if (!m_displayBound) |
304 | qCDebug(qLcWaylandCompositorHardwareIntegration) << "Wayland display already bound by other client buffer integration." ; |
305 | m_wlDisplay = display; |
306 | } |
307 | |
308 | // request and sent formats/modifiers only after egl_display is bound |
309 | QHash<uint32_t, QList<uint64_t>> modifiers; |
310 | for (const auto &format : supportedDrmFormats()) { |
311 | modifiers[format] = supportedDrmModifiers(format); |
312 | } |
313 | m_linuxDmabuf->setSupportedModifiers(modifiers); |
314 | } |
315 | |
316 | QList<uint32_t> LinuxDmabufClientBufferIntegration::supportedDrmFormats() |
317 | { |
318 | if (!egl_query_dmabuf_formats_ext) |
319 | return QList<uint32_t>(); |
320 | |
321 | // request total number of formats |
322 | EGLint count = 0; |
323 | EGLBoolean success = egl_query_dmabuf_formats_ext(m_eglDisplay, 0, nullptr, &count); |
324 | |
325 | if (success && count > 0) { |
326 | QList<uint32_t> drmFormats(count); |
327 | if (egl_query_dmabuf_formats_ext(m_eglDisplay, count, (EGLint *) drmFormats.data(), &count)) |
328 | return drmFormats; |
329 | } |
330 | |
331 | return QList<uint32_t>(); |
332 | } |
333 | |
334 | QList<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32_t format) |
335 | { |
336 | if (!egl_query_dmabuf_modifiers_ext) |
337 | return QList<uint64_t>(); |
338 | |
339 | // request total number of formats |
340 | EGLint count = 0; |
341 | EGLBoolean success = egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, 0, nullptr, nullptr, &count); |
342 | |
343 | if (success && count > 0) { |
344 | QList<uint64_t> modifiers(count); |
345 | if (egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, count, modifiers.data(), nullptr, &count)) { |
346 | return modifiers; |
347 | } |
348 | } |
349 | |
350 | return QList<uint64_t>(); |
351 | } |
352 | |
353 | |
354 | void LinuxDmabufClientBufferIntegration::deleteGLTextureWhenPossible(QOpenGLTexture *texture, QOpenGLContext *ctx) { |
355 | QMutexLocker locker(&m_orphanedTexturesLock); |
356 | |
357 | Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size()); |
358 | |
359 | m_orphanedTextures << texture; |
360 | m_orphanedTexturesAboutToBeDestroyedConnection << QObject::connect(sender: ctx, signal: &QOpenGLContext::aboutToBeDestroyed, |
361 | context: ctx, slot: [this, texture]() { |
362 | this->deleteSpecificOrphanedTexture(texture); |
363 | }, type: Qt::DirectConnection); |
364 | } |
365 | |
366 | |
367 | void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures() |
368 | { |
369 | Q_ASSERT(QOpenGLContext::currentContext()); |
370 | |
371 | QMutexLocker locker(&m_orphanedTexturesLock); |
372 | |
373 | if (!m_orphanedTextures.isEmpty()) |
374 | qCDebug(qLcWaylandCompositorHardwareIntegration) << "About to delete some textures: " |
375 | << m_orphanedTextures; |
376 | |
377 | qDeleteAll(c: m_orphanedTextures); |
378 | |
379 | for (QMetaObject::Connection con : m_orphanedTexturesAboutToBeDestroyedConnection) |
380 | QObject::disconnect(con); |
381 | |
382 | m_orphanedTexturesAboutToBeDestroyedConnection.clear(); |
383 | m_orphanedTextures.clear(); |
384 | } |
385 | |
386 | void LinuxDmabufClientBufferIntegration::deleteSpecificOrphanedTexture(QOpenGLTexture *texture) |
387 | { |
388 | Q_ASSERT(m_orphanedTextures.size() == m_orphanedTexturesAboutToBeDestroyedConnection.size()); |
389 | |
390 | QMutexLocker locker(&m_orphanedTexturesLock); |
391 | |
392 | // In this case, deleteOrphanedTextures was called while we entered (see lock!) this function! |
393 | if (m_orphanedTextures.length()==0) { |
394 | qCWarning(qLcWaylandCompositorHardwareIntegration) |
395 | << Q_FUNC_INFO |
396 | << "Looks like deleteOrphanedTextures() and this function where called simultaneously!" |
397 | << "This might cause issues!" ; |
398 | return; |
399 | } |
400 | |
401 | int i = m_orphanedTextures.indexOf(t: texture); |
402 | Q_ASSERT(i!=-1); // If it isn't empty (see above if), then it should be guaranteed to still contain this texture |
403 | |
404 | m_orphanedTextures.removeAt(i); |
405 | QMetaObject::Connection con = m_orphanedTexturesAboutToBeDestroyedConnection.takeAt(i); |
406 | |
407 | QObject::disconnect(con); |
408 | delete texture; |
409 | |
410 | qCDebug(qLcWaylandCompositorHardwareIntegration) |
411 | << Q_FUNC_INFO |
412 | << "texture deleted due to QOpenGLContext::aboutToBeDestroyed!" |
413 | << "Pointer (now dead) was:" << (void*)texture; |
414 | } |
415 | |
416 | void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image) |
417 | { |
418 | egl_destroy_image(m_eglDisplay, image); |
419 | } |
420 | |
421 | QtWayland::ClientBuffer *LinuxDmabufClientBufferIntegration::createBufferFor(wl_resource *resource) |
422 | { |
423 | auto it = m_importedBuffers.find(key: resource); |
424 | if (it != m_importedBuffers.end()) { |
425 | m_importedBuffers.value(key: resource); |
426 | return new LinuxDmabufClientBuffer(this, it.value()->resource()->handle, m_importedBuffers.value(key: resource)); |
427 | } |
428 | |
429 | return nullptr; |
430 | } |
431 | |
432 | bool LinuxDmabufClientBufferIntegration::importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer) |
433 | { |
434 | if (m_importedBuffers.contains(key: resource)) { |
435 | qCWarning(qLcWaylandCompositorHardwareIntegration) << "buffer has already been added" ; |
436 | return false; |
437 | } |
438 | m_importedBuffers[resource] = linuxDmabufBuffer; |
439 | if (m_yuvFormats.contains(key: linuxDmabufBuffer->drmFormat())) |
440 | return initYuvTexture(dmabufBuffer: linuxDmabufBuffer); |
441 | else |
442 | return initSimpleTexture(dmabufBuffer: linuxDmabufBuffer); |
443 | } |
444 | |
445 | void LinuxDmabufClientBufferIntegration::removeBuffer(wl_resource *resource) |
446 | { |
447 | m_importedBuffers.remove(key: resource); |
448 | } |
449 | |
450 | LinuxDmabufClientBuffer::LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration *integration, |
451 | wl_resource *bufferResource, |
452 | LinuxDmabufWlBuffer *dmabufBuffer) |
453 | : ClientBuffer(bufferResource) |
454 | , m_integration(integration) |
455 | { |
456 | d = dmabufBuffer; |
457 | } |
458 | |
459 | QOpenGLTexture *LinuxDmabufClientBuffer::toOpenGlTexture(int plane) |
460 | { |
461 | // At this point we should have a valid OpenGL context, so it's safe to destroy textures |
462 | m_integration->deleteOrphanedTextures(); |
463 | |
464 | if (!m_buffer) |
465 | return nullptr; |
466 | |
467 | QOpenGLTexture *texture = d->texture(plane); |
468 | |
469 | const auto target = static_cast<QOpenGLTexture::Target>(GL_TEXTURE_2D); |
470 | |
471 | if (!texture) { |
472 | texture = new QOpenGLTexture(target); |
473 | texture->setFormat(openGLFormatFromBufferFormat(format: formatFromDrmFormat(format: d->drmFormat()))); |
474 | texture->setSize(width: d->size().width(), height: d->size().height()); |
475 | texture->create(); |
476 | d->initTexture(plane, texture); |
477 | } |
478 | |
479 | if (m_textureDirty) { |
480 | m_textureDirty = false; |
481 | texture->bind(); |
482 | glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
483 | m_integration->gl_egl_image_target_texture_2d(target, d->image(plane)); |
484 | } |
485 | return texture; |
486 | } |
487 | |
488 | void LinuxDmabufClientBuffer::setDestroyed() |
489 | { |
490 | m_integration->removeBuffer(resource: m_buffer); |
491 | ClientBuffer::setDestroyed(); |
492 | } |
493 | |
494 | LinuxDmabufClientBuffer::~LinuxDmabufClientBuffer() |
495 | { |
496 | // resources are deleted by buffer_destroy_resource |
497 | m_buffer = nullptr; |
498 | d = nullptr; |
499 | } |
500 | |
501 | QWaylandBufferRef::BufferFormatEgl LinuxDmabufClientBuffer::bufferFormatEgl() const |
502 | { |
503 | return formatFromDrmFormat(format: d->drmFormat()); |
504 | } |
505 | |
506 | QSize LinuxDmabufClientBuffer::size() const |
507 | { |
508 | return d->size(); |
509 | } |
510 | |
511 | QWaylandSurface::Origin LinuxDmabufClientBuffer::origin() const |
512 | { |
513 | return (d->flags() & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) ? QWaylandSurface::OriginBottomLeft : QWaylandSurface::OriginTopLeft; |
514 | } |
515 | |
516 | QT_END_NAMESPACE |
517 | |