15#include <QOpenGLContext>
16#include <drm_fourcc.h>
19#ifndef EGL_DRM_RENDER_NODE_FILE_EXT
20#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
26static bool isOpenGLES()
28 if (qstrcmp(qgetenv(
"KWIN_COMPOSE"),
"O2ES") == 0) {
31 return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
40 if (eglInitialize(display, &major, &minor) == EGL_FALSE) {
41 qCWarning(KWIN_OPENGL) <<
"eglInitialize failed";
42 EGLint error = eglGetError();
43 if (error != EGL_SUCCESS) {
44 qCWarning(KWIN_OPENGL) <<
"Error during eglInitialize " << error;
48 EGLint error = eglGetError();
49 if (error != EGL_SUCCESS) {
50 qCWarning(KWIN_OPENGL) <<
"Error during eglInitialize " << error;
53 qCDebug(KWIN_OPENGL) <<
"Egl Initialize succeeded";
54 if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) {
55 qCCritical(KWIN_OPENGL) <<
"bind OpenGL API failed";
58 qCDebug(KWIN_OPENGL) <<
"EGL version: " << major <<
"." << minor;
60 const auto extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(
' ');
62 const QByteArray requiredExtensions[] = {
63 QByteArrayLiteral(
"EGL_KHR_no_config_context"),
64 QByteArrayLiteral(
"EGL_KHR_surfaceless_context"),
66 for (
const QByteArray &extensionName : requiredExtensions) {
68 qCWarning(KWIN_OPENGL) << extensionName <<
"extension is unsupported";
73 return std::make_unique<EglDisplay>(display,
extensions, owning);
78 , m_extensions(extensions)
80 , m_supportsBufferAge(extensions.contains(QByteArrayLiteral(
"EGL_EXT_buffer_age")) && qgetenv(
"KWIN_USE_BUFFER_AGE") !=
"0")
81 , m_supportsNativeFence(extensions.contains(QByteArrayLiteral(
"EGL_ANDROID_native_fence_sync")))
82 , m_importFormats(queryImportFormats())
89 eglTerminate(m_handle);
105 return m_extensions.contains(name);
108static bool checkExtension(
const QByteArrayView extensions,
const QByteArrayView extension)
110 for (
int i = 0; i < extensions.size();) {
111 if (extensions[i] ==
' ') {
115 int next = extensions.indexOf(
' ', i);
117 next = extensions.size();
120 const int size = next - i;
121 if (extension.size() == size && extensions.sliced(i, size) == extension) {
133 const char *clientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
134 if (checkExtension(clientExtensions,
"EGL_EXT_device_query")) {
135 EGLAttrib eglDeviceAttrib;
136 if (eglQueryDisplayAttribEXT(m_handle, EGL_DEVICE_EXT, &eglDeviceAttrib)) {
137 EGLDeviceEXT eglDevice =
reinterpret_cast<EGLDeviceEXT
>(eglDeviceAttrib);
139 const char *deviceExtensions = eglQueryDeviceStringEXT(eglDevice, EGL_EXTENSIONS);
140 if (checkExtension(deviceExtensions,
"EGL_EXT_device_drm_render_node")) {
142 return QString::fromLocal8Bit(node);
145 if (checkExtension(deviceExtensions,
"EGL_EXT_device_drm")) {
147 if (
const char *node = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_DEVICE_FILE_EXT)) {
148 return QString::fromLocal8Bit(node);
158 return m_supportsBufferAge;
163 return m_supportsNativeFence;
168 QList<EGLint> attribs;
169 attribs.reserve(6 + dmabuf.
planeCount * 10 + 1);
171 attribs << EGL_WIDTH << dmabuf.
width
172 << EGL_HEIGHT << dmabuf.
height
173 << EGL_LINUX_DRM_FOURCC_EXT << dmabuf.
format;
175 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.
fd[0].get()
176 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.
offset[0]
177 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.
pitch[0];
178 if (dmabuf.
modifier != DRM_FORMAT_MOD_INVALID) {
179 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.
modifier & 0xffffffff)
180 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.
modifier >> 32);
184 attribs << EGL_DMA_BUF_PLANE1_FD_EXT << dmabuf.
fd[1].get()
185 << EGL_DMA_BUF_PLANE1_OFFSET_EXT << dmabuf.
offset[1]
186 << EGL_DMA_BUF_PLANE1_PITCH_EXT << dmabuf.
pitch[1];
187 if (dmabuf.
modifier != DRM_FORMAT_MOD_INVALID) {
188 attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(dmabuf.
modifier & 0xffffffff)
189 << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(dmabuf.
modifier >> 32);
194 attribs << EGL_DMA_BUF_PLANE2_FD_EXT << dmabuf.
fd[2].get()
195 << EGL_DMA_BUF_PLANE2_OFFSET_EXT << dmabuf.
offset[2]
196 << EGL_DMA_BUF_PLANE2_PITCH_EXT << dmabuf.
pitch[2];
197 if (dmabuf.
modifier != DRM_FORMAT_MOD_INVALID) {
198 attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(dmabuf.
modifier & 0xffffffff)
199 << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(dmabuf.
modifier >> 32);
204 attribs << EGL_DMA_BUF_PLANE3_FD_EXT << dmabuf.
fd[3].get()
205 << EGL_DMA_BUF_PLANE3_OFFSET_EXT << dmabuf.
offset[3]
206 << EGL_DMA_BUF_PLANE3_PITCH_EXT << dmabuf.
pitch[3];
207 if (dmabuf.
modifier != DRM_FORMAT_MOD_INVALID) {
208 attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(dmabuf.
modifier & 0xffffffff)
209 << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(dmabuf.
modifier >> 32);
215 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
nullptr, attribs.data());
220 QList<EGLint> attribs;
221 attribs.reserve(6 + 1 * 10 + 1);
223 attribs << EGL_WIDTH << size.width()
224 << EGL_HEIGHT << size.height()
225 << EGL_LINUX_DRM_FOURCC_EXT <<
format;
227 attribs << EGL_DMA_BUF_PLANE0_FD_EXT << dmabuf.
fd[plane].get()
228 << EGL_DMA_BUF_PLANE0_OFFSET_EXT << dmabuf.
offset[plane]
229 << EGL_DMA_BUF_PLANE0_PITCH_EXT << dmabuf.
pitch[plane];
230 if (dmabuf.
modifier != DRM_FORMAT_MOD_INVALID) {
231 attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(dmabuf.
modifier & 0xffffffff)
232 << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(dmabuf.
modifier >> 32);
236 return eglCreateImageKHR(m_handle, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
nullptr, attribs.data());
241 return m_importFormats;
246 QHash<uint32_t, QList<uint64_t>> ret;
247 ret.reserve(m_importFormats.size());
248 for (
auto it = m_importFormats.constBegin(), itEnd = m_importFormats.constEnd(); it != itEnd; ++it) {
249 ret[it.key()] = it->nonExternalOnlyModifiers;
256 if (
const auto it = m_importFormats.find(
format); it != m_importFormats.end()) {
257 return it->externalOnlyModifiers.contains(modifier);
263QHash<uint32_t, EglDisplay::DrmFormatInfo> EglDisplay::queryImportFormats()
const
265 if (!
hasExtension(QByteArrayLiteral(
"EGL_EXT_image_dma_buf_import")) || !
hasExtension(QByteArrayLiteral(
"EGL_EXT_image_dma_buf_import_modifiers"))) {
269 typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
270 typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, EGLint
format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
271 eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT =
nullptr;
272 eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT =
nullptr;
273 eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)eglGetProcAddress(
"eglQueryDmaBufFormatsEXT");
274 eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func)eglGetProcAddress(
"eglQueryDmaBufModifiersEXT");
275 if (eglQueryDmaBufFormatsEXT ==
nullptr) {
280 EGLBoolean success = eglQueryDmaBufFormatsEXT(m_handle, 0,
nullptr, &count);
281 if (!success || count == 0) {
282 qCCritical(KWIN_OPENGL) <<
"eglQueryDmaBufFormatsEXT failed!" << getEglErrorString();
285 QList<uint32_t> formats(count);
286 if (!eglQueryDmaBufFormatsEXT(m_handle, count, (EGLint *)formats.data(), &count)) {
287 qCCritical(KWIN_OPENGL) <<
"eglQueryDmaBufFormatsEXT with count" << count <<
"failed!" << getEglErrorString();
290 QHash<uint32_t, DrmFormatInfo> ret;
291 for (
const auto format : std::as_const(formats)) {
292 if (eglQueryDmaBufModifiersEXT !=
nullptr) {
294 const EGLBoolean success = eglQueryDmaBufModifiersEXT(m_handle,
format, 0,
nullptr,
nullptr, &count);
295 if (success && count > 0) {
296 DrmFormatInfo drmFormatInfo;
297 drmFormatInfo.allModifiers.resize(count);
298 QList<EGLBoolean> externalOnly(count);
299 if (eglQueryDmaBufModifiersEXT(m_handle,
format, count, drmFormatInfo.allModifiers.data(), externalOnly.data(), &count)) {
300 drmFormatInfo.externalOnlyModifiers = drmFormatInfo.allModifiers;
301 drmFormatInfo.nonExternalOnlyModifiers = drmFormatInfo.allModifiers;
302 for (
int i = drmFormatInfo.allModifiers.size() - 1; i >= 0; i--) {
303 if (externalOnly[i]) {
304 drmFormatInfo.nonExternalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]);
306 drmFormatInfo.externalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]);
309 if (!drmFormatInfo.allModifiers.empty()) {
310 if (!drmFormatInfo.allModifiers.contains(DRM_FORMAT_MOD_INVALID)) {
311 drmFormatInfo.allModifiers.push_back(DRM_FORMAT_MOD_INVALID);
312 drmFormatInfo.nonExternalOnlyModifiers.push_back(DRM_FORMAT_MOD_INVALID);
314 ret.insert(
format, drmFormatInfo);
320 DrmFormatInfo drmFormat;
321 drmFormat.allModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR};
322 drmFormat.nonExternalOnlyModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR};
323 ret.insert(
format, drmFormat);
bool hasExtension(const QByteArray &name) const
EglDisplay(::EGLDisplay display, const QList< QByteArray > &extensions, bool owning=true)
QString renderNode() const
bool supportsNativeFence() const
QList< QByteArray > extensions() const
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
static std::unique_ptr< EglDisplay > create(::EGLDisplay display, bool owning=true)
QHash< uint32_t, QList< uint64_t > > nonExternalOnlySupportedDrmFormats() const
QHash< uint32_t, DrmFormatInfo > allSupportedDrmFormats() const
bool supportsBufferAge() const
bool isExternalOnly(uint32_t format, uint64_t modifier) const
::EGLDisplay handle() const
#define EGL_DRM_RENDER_NODE_FILE_EXT
std::array< uint32_t, 4 > offset
std::array< uint32_t, 4 > pitch
std::array< FileDescriptor, 4 > fd