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