File indexing completed on 2024-11-10 04:56:53
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "egldisplay.h" 0010 #include "core/graphicsbuffer.h" 0011 #include "opengl/eglutils_p.h" 0012 #include "opengl/glutils.h" 0013 #include "utils/common.h" 0014 0015 #include <QOpenGLContext> 0016 #include <drm_fourcc.h> 0017 #include <utils/drm_format_helper.h> 0018 0019 #ifndef EGL_DRM_RENDER_NODE_FILE_EXT 0020 #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377 0021 #endif 0022 0023 namespace KWin 0024 { 0025 0026 static bool isOpenGLES() 0027 { 0028 if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { 0029 return true; 0030 } 0031 return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; 0032 } 0033 0034 std::unique_ptr<EglDisplay> EglDisplay::create(::EGLDisplay display, bool owning) 0035 { 0036 if (!display) { 0037 return nullptr; 0038 } 0039 EGLint major, minor; 0040 if (eglInitialize(display, &major, &minor) == EGL_FALSE) { 0041 qCWarning(KWIN_OPENGL) << "eglInitialize failed"; 0042 EGLint error = eglGetError(); 0043 if (error != EGL_SUCCESS) { 0044 qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error; 0045 } 0046 return nullptr; 0047 } 0048 EGLint error = eglGetError(); 0049 if (error != EGL_SUCCESS) { 0050 qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error; 0051 return nullptr; 0052 } 0053 qCDebug(KWIN_OPENGL) << "Egl Initialize succeeded"; 0054 if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) { 0055 qCCritical(KWIN_OPENGL) << "bind OpenGL API failed"; 0056 return nullptr; 0057 } 0058 qCDebug(KWIN_OPENGL) << "EGL version: " << major << "." << minor; 0059 0060 const auto extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' '); 0061 0062 const QByteArray requiredExtensions[] = { 0063 QByteArrayLiteral("EGL_KHR_no_config_context"), 0064 QByteArrayLiteral("EGL_KHR_surfaceless_context"), 0065 }; 0066 for (const QByteArray &extensionName : requiredExtensions) { 0067 if (!extensions.contains(extensionName)) { 0068 qCWarning(KWIN_OPENGL) << extensionName << "extension is unsupported"; 0069 return nullptr; 0070 } 0071 } 0072 0073 return std::make_unique<EglDisplay>(display, extensions, owning); 0074 } 0075 0076 EglDisplay::EglDisplay(::EGLDisplay display, const QList<QByteArray> &extensions, bool owning) 0077 : m_handle(display) 0078 , m_extensions(extensions) 0079 , m_owning(owning) 0080 , m_supportsBufferAge(extensions.contains(QByteArrayLiteral("EGL_EXT_buffer_age")) && qgetenv("KWIN_USE_BUFFER_AGE") != "0") 0081 , m_supportsNativeFence(extensions.contains(QByteArrayLiteral("EGL_ANDROID_native_fence_sync"))) 0082 , m_importFormats(queryImportFormats()) 0083 { 0084 } 0085 0086 EglDisplay::~EglDisplay() 0087 { 0088 if (m_owning) { 0089 eglTerminate(m_handle); 0090 } 0091 } 0092 0093 QList<QByteArray> EglDisplay::extensions() const 0094 { 0095 return m_extensions; 0096 } 0097 0098 ::EGLDisplay EglDisplay::handle() const 0099 { 0100 return m_handle; 0101 } 0102 0103 bool EglDisplay::hasExtension(const QByteArray &name) const 0104 { 0105 return m_extensions.contains(name); 0106 } 0107 0108 static bool checkExtension(const QByteArrayView extensions, const QByteArrayView extension) 0109 { 0110 for (int i = 0; i < extensions.size();) { 0111 if (extensions[i] == ' ') { 0112 i++; 0113 continue; 0114 } 0115 int next = extensions.indexOf(' ', i); 0116 if (next == -1) { 0117 next = extensions.size(); 0118 } 0119 0120 const int size = next - i; 0121 if (extension.size() == size && extensions.sliced(i, size) == extension) { 0122 return true; 0123 } 0124 0125 i = next; 0126 } 0127 0128 return false; 0129 } 0130 0131 QString EglDisplay::renderNode() const 0132 { 0133 const char *clientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); 0134 if (checkExtension(clientExtensions, "EGL_EXT_device_query")) { 0135 EGLAttrib eglDeviceAttrib; 0136 if (eglQueryDisplayAttribEXT(m_handle, EGL_DEVICE_EXT, &eglDeviceAttrib)) { 0137 EGLDeviceEXT eglDevice = reinterpret_cast<EGLDeviceEXT>(eglDeviceAttrib); 0138 0139 const char *deviceExtensions = eglQueryDeviceStringEXT(eglDevice, EGL_EXTENSIONS); 0140 if (checkExtension(deviceExtensions, "EGL_EXT_device_drm_render_node")) { 0141 if (const char *node = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_RENDER_NODE_FILE_EXT)) { 0142 return QString::fromLocal8Bit(node); 0143 } 0144 } 0145 if (checkExtension(deviceExtensions, "EGL_EXT_device_drm")) { 0146 // Fallback to display device. 0147 if (const char *node = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_DEVICE_FILE_EXT)) { 0148 return QString::fromLocal8Bit(node); 0149 } 0150 } 0151 } 0152 } 0153 return QString(); 0154 } 0155 0156 bool EglDisplay::supportsBufferAge() const 0157 { 0158 return m_supportsBufferAge; 0159 } 0160 0161 bool EglDisplay::supportsNativeFence() const 0162 { 0163 return m_supportsNativeFence; 0164 } 0165 0166 EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf) const 0167 { 0168 QList<EGLint> attribs; 0169 attribs.reserve(6 + dmabuf.planeCount * 10 + 1); 0170 0171 attribs << EGL_WIDTH << dmabuf.width 0172 << EGL_HEIGHT << dmabuf.height 0173 << EGL_LINUX_DRM_FOURCC_EXT << dmabuf.format; 0174 0175 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[0].get() 0176 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[0] 0177 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[0]; 0178 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { 0179 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) 0180 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); 0181 } 0182 0183 if (dmabuf.planeCount > 1) { 0184 attribs << EGL_DMA_BUF_PLANE1_FD_EXT << dmabuf.fd[1].get() 0185 << EGL_DMA_BUF_PLANE1_OFFSET_EXT << dmabuf.offset[1] 0186 << EGL_DMA_BUF_PLANE1_PITCH_EXT << dmabuf.pitch[1]; 0187 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { 0188 attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) 0189 << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); 0190 } 0191 } 0192 0193 if (dmabuf.planeCount > 2) { 0194 attribs << EGL_DMA_BUF_PLANE2_FD_EXT << dmabuf.fd[2].get() 0195 << EGL_DMA_BUF_PLANE2_OFFSET_EXT << dmabuf.offset[2] 0196 << EGL_DMA_BUF_PLANE2_PITCH_EXT << dmabuf.pitch[2]; 0197 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { 0198 attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) 0199 << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); 0200 } 0201 } 0202 0203 if (dmabuf.planeCount > 3) { 0204 attribs << EGL_DMA_BUF_PLANE3_FD_EXT << dmabuf.fd[3].get() 0205 << EGL_DMA_BUF_PLANE3_OFFSET_EXT << dmabuf.offset[3] 0206 << EGL_DMA_BUF_PLANE3_PITCH_EXT << dmabuf.pitch[3]; 0207 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { 0208 attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) 0209 << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); 0210 } 0211 } 0212 0213 attribs << EGL_NONE; 0214 0215 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); 0216 } 0217 0218 EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int plane, int format, const QSize &size) const 0219 { 0220 QList<EGLint> attribs; 0221 attribs.reserve(6 + 1 * 10 + 1); 0222 0223 attribs << EGL_WIDTH << size.width() 0224 << EGL_HEIGHT << size.height() 0225 << EGL_LINUX_DRM_FOURCC_EXT << format; 0226 0227 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.fd[plane].get() 0228 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.offset[plane] 0229 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.pitch[plane]; 0230 if (dmabuf.modifier != DRM_FORMAT_MOD_INVALID) { 0231 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.modifier & 0xffffffff) 0232 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.modifier >> 32); 0233 } 0234 attribs << EGL_NONE; 0235 0236 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); 0237 } 0238 0239 QHash<uint32_t, EglDisplay::DrmFormatInfo> EglDisplay::allSupportedDrmFormats() const 0240 { 0241 return m_importFormats; 0242 } 0243 0244 QHash<uint32_t, QList<uint64_t>> EglDisplay::nonExternalOnlySupportedDrmFormats() const 0245 { 0246 QHash<uint32_t, QList<uint64_t>> ret; 0247 ret.reserve(m_importFormats.size()); 0248 for (auto it = m_importFormats.constBegin(), itEnd = m_importFormats.constEnd(); it != itEnd; ++it) { 0249 ret[it.key()] = it->nonExternalOnlyModifiers; 0250 } 0251 return ret; 0252 } 0253 0254 bool EglDisplay::isExternalOnly(uint32_t format, uint64_t modifier) const 0255 { 0256 if (const auto it = m_importFormats.find(format); it != m_importFormats.end()) { 0257 return it->externalOnlyModifiers.contains(modifier); 0258 } else { 0259 return false; 0260 } 0261 } 0262 0263 QHash<uint32_t, EglDisplay::DrmFormatInfo> EglDisplay::queryImportFormats() const 0264 { 0265 if (!hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import")) || !hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) { 0266 return {}; 0267 } 0268 0269 typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats); 0270 typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers); 0271 eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT = nullptr; 0272 eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT = nullptr; 0273 eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)eglGetProcAddress("eglQueryDmaBufFormatsEXT"); 0274 eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func)eglGetProcAddress("eglQueryDmaBufModifiersEXT"); 0275 if (eglQueryDmaBufFormatsEXT == nullptr) { 0276 return {}; 0277 } 0278 0279 EGLint count = 0; 0280 EGLBoolean success = eglQueryDmaBufFormatsEXT(m_handle, 0, nullptr, &count); 0281 if (!success || count == 0) { 0282 qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT failed!" << getEglErrorString(); 0283 return {}; 0284 } 0285 QList<uint32_t> formats(count); 0286 if (!eglQueryDmaBufFormatsEXT(m_handle, count, (EGLint *)formats.data(), &count)) { 0287 qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT with count" << count << "failed!" << getEglErrorString(); 0288 return {}; 0289 } 0290 QHash<uint32_t, DrmFormatInfo> ret; 0291 for (const auto format : std::as_const(formats)) { 0292 if (eglQueryDmaBufModifiersEXT != nullptr) { 0293 EGLint count = 0; 0294 const EGLBoolean success = eglQueryDmaBufModifiersEXT(m_handle, format, 0, nullptr, nullptr, &count); 0295 if (success && count > 0) { 0296 DrmFormatInfo drmFormatInfo; 0297 drmFormatInfo.allModifiers.resize(count); 0298 QList<EGLBoolean> externalOnly(count); 0299 if (eglQueryDmaBufModifiersEXT(m_handle, format, count, drmFormatInfo.allModifiers.data(), externalOnly.data(), &count)) { 0300 drmFormatInfo.externalOnlyModifiers = drmFormatInfo.allModifiers; 0301 drmFormatInfo.nonExternalOnlyModifiers = drmFormatInfo.allModifiers; 0302 for (int i = drmFormatInfo.allModifiers.size() - 1; i >= 0; i--) { 0303 if (externalOnly[i]) { 0304 drmFormatInfo.nonExternalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]); 0305 } else { 0306 drmFormatInfo.externalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]); 0307 } 0308 } 0309 if (!drmFormatInfo.allModifiers.empty()) { 0310 if (!drmFormatInfo.allModifiers.contains(DRM_FORMAT_MOD_INVALID)) { 0311 drmFormatInfo.allModifiers.push_back(DRM_FORMAT_MOD_INVALID); 0312 drmFormatInfo.nonExternalOnlyModifiers.push_back(DRM_FORMAT_MOD_INVALID); 0313 } 0314 ret.insert(format, drmFormatInfo); 0315 } 0316 continue; 0317 } 0318 } 0319 } 0320 DrmFormatInfo drmFormat; 0321 drmFormat.allModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}; 0322 drmFormat.nonExternalOnlyModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}; 0323 ret.insert(format, drmFormat); 0324 } 0325 return ret; 0326 } 0327 0328 }