diff --git a/CMakeLists.txt b/CMakeLists.txt index 357aaa9bc..115c930bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,14 @@ set_package_properties(OpenGL PROPERTIES DESCRIPTION "The OpenGL libraries" URL "http://www.opengl.org" TYPE OPTIONAL ) + +find_package(EGL) +set_package_properties(EGL PROPERTIES + PURPOSE "Support for Window Thumbnail on EGL platform" + TYPE OPTIONAL + ) +set(HAVE_EGL ${EGL_FOUND}) + if(OPENGL_FOUND AND (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL")) set(HAVE_GLX ${HAVE_X11}) else() diff --git a/src/declarativeimports/core/CMakeLists.txt b/src/declarativeimports/core/CMakeLists.txt index 97e928303..fb518034f 100644 --- a/src/declarativeimports/core/CMakeLists.txt +++ b/src/declarativeimports/core/CMakeLists.txt @@ -66,6 +66,14 @@ if(HAVE_X11) endif() endif() +if(HAVE_EGL) + target_link_libraries(corebindingsplugin EGL::EGL) +endif() + +if (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GLESv2") + target_link_libraries(corebindingsplugin Qt5::Gui_GLESv2) +endif() + install(TARGETS corebindingsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core) diff --git a/src/declarativeimports/core/windowthumbnail.cpp b/src/declarativeimports/core/windowthumbnail.cpp index b96d939b2..e1bda70c3 100644 --- a/src/declarativeimports/core/windowthumbnail.cpp +++ b/src/declarativeimports/core/windowthumbnail.cpp @@ -34,6 +34,11 @@ typedef void (*glXBindTexImageEXT_func)(Display* dpy, GLXDrawable drawable, int buffer, const int* attrib_list); typedef void (*glXReleaseTexImageEXT_func)(Display* dpy, GLXDrawable drawable, int buffer); #endif +#if HAVE_EGL +typedef EGLImageKHR(*eglCreateImageKHR_func)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*); +typedef EGLBoolean(*eglDestroyImageKHR_func)(EGLDisplay, EGLImageKHR); +typedef GLvoid(*glEGLImageTargetTexture2DOES_func)(GLenum, GLeglImageOES); +#endif // HAVE_EGL #endif namespace Plasma { @@ -67,7 +72,11 @@ WindowThumbnail::WindowThumbnail(QQuickItem* parent) , m_texture(0) #if HAVE_GLX , m_glxPixmap(XCB_PIXMAP_NONE) -#endif +#endif // HAVE_GLX +#if HAVE_EGL + , m_eglFunctionsResolved(false) + , m_image(EGL_NO_IMAGE_KHR) +#endif // HAVE_EGL #endif { setFlag(ItemHasContents); @@ -189,36 +198,23 @@ void WindowThumbnail::iconToTexture(WindowTextureNode *textureNode) textureNode->reset(window()->createTextureFromImage(image)); } -void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode) -{ - if (!m_damaged && textureNode->texture()) { - return; - } #if HAVE_XCB_COMPOSITE - xcb_connection_t *c = QX11Info::connection(); - if (m_pixmap == XCB_PIXMAP_NONE) { - m_pixmap = pixmapForWindow(); - } - if (m_pixmap == XCB_PIXMAP_NONE) { - // create above failed - iconToTexture(textureNode); - return; - } #if HAVE_GLX +bool WindowThumbnail::windowToTextureGLX(WindowTextureNode *textureNode) +{ if (glXGetCurrentContext()) { if (!m_openGLFunctionsResolved) { resolveGLXFunctions(); } if (!m_bindTexImage || !m_releaseTexImage) { - iconToTexture(textureNode); - return; + return false; } if (m_glxPixmap == XCB_PIXMAP_NONE) { + xcb_connection_t *c = QX11Info::connection(); auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap); if (!loadGLXTexture()) { - iconToTexture(textureNode); - return; + return false; } QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR)); @@ -231,14 +227,114 @@ void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode) } textureNode->texture()->bind(); bindGLXTexture(); - } else { -#else - { -#endif + return true; + } + return false; +} +#endif // HAVE_GLX + +#if HAVE_EGL +bool WindowThumbnail::xcbWindowToTextureEGL(WindowTextureNode *textureNode) +{ + EGLContext context = eglGetCurrentContext(); + if (context != EGL_NO_CONTEXT) { + if (!m_eglFunctionsResolved) { + resolveEGLFunctions(); + } + if (!m_eglCreateImageKHR || !m_eglDestroyImageKHR || !m_glEGLImageTargetTexture2DOES) { + return false; + } + if (m_image == EGL_NO_IMAGE_KHR) { + xcb_connection_t *c = QX11Info::connection(); + auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap); + + const EGLint attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + m_image = ((eglCreateImageKHR_func)(m_eglCreateImageKHR))(eglGetCurrentDisplay(), EGL_NO_CONTEXT, + EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)m_pixmap, attribs); + + if (m_image == EGL_NO_IMAGE_KHR) { + qDebug() << "failed to create egl image"; + return false; + } + + glGenTextures(1, &m_texture); + QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR)); + QSize size; + if (!geo.isNull()) { + size.setWidth(geo->width); + size.setHeight(geo->height); + } + textureNode->reset(window()->createTextureFromId(m_texture, size)); + } + textureNode->texture()->bind(); + bindEGLTexture(); + return true; + } + return false; +} + +void WindowThumbnail::resolveEGLFunctions() +{ + EGLDisplay display = eglGetCurrentDisplay(); + if (display == EGL_NO_DISPLAY) { + return; + } + auto *context = window()->openglContext(); + QList extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' '); + if (extensions.contains(QByteArrayLiteral("EGL_KHR_image")) || + (extensions.contains(QByteArrayLiteral("EGL_KHR_image_base")) && + extensions.contains(QByteArrayLiteral("EGL_KHR_image_pixmap")))) { + if (context->hasExtension(QByteArrayLiteral("GL_OES_EGL_image"))) { + qDebug() << "Have EGL texture from pixmap"; + m_eglCreateImageKHR = context->getProcAddress(QByteArrayLiteral("eglCreateImageKHR")); + m_eglDestroyImageKHR = context->getProcAddress(QByteArrayLiteral("eglDestroyImageKHR")); + m_glEGLImageTargetTexture2DOES = context->getProcAddress(QByteArrayLiteral("glEGLImageTargetTexture2DOES")); + } + } + m_eglFunctionsResolved = true; +} + +void WindowThumbnail::bindEGLTexture() +{ + ((glEGLImageTargetTexture2DOES_func)(m_glEGLImageTargetTexture2DOES))(GL_TEXTURE_2D, (GLeglImageOES)m_image); + resetDamaged(); +} +#endif // HAVE_EGL + +#endif // HAVE_XCB_COMPOSITE + +void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode) +{ + if (!m_damaged && textureNode->texture()) { + return; + } +#if HAVE_XCB_COMPOSITE + if (m_pixmap == XCB_PIXMAP_NONE) { + m_pixmap = pixmapForWindow(); + } + if (m_pixmap == XCB_PIXMAP_NONE) { + // create above failed + iconToTexture(textureNode); + return; + } + bool fallbackToIcon = true; +#if HAVE_GLX + fallbackToIcon = !windowToTextureGLX(textureNode); +#endif // HAVE_GLX +#if HAVE_EGL + if (fallbackToIcon) { + // if glx succeeded fallbackToIcon is false, thus we shouldn't try egl + fallbackToIcon = !xcbWindowToTextureEGL(textureNode); + } +#endif // HAVE_EGL + if (fallbackToIcon) { // just for safety to not crash iconToTexture(textureNode); } - // TODO: add an egl variant textureNode->markDirty(QSGNode::DirtyForceUpdate); #endif } @@ -393,6 +489,13 @@ void WindowThumbnail::discardPixmap() m_glxPixmap = XCB_PIXMAP_NONE; glDeleteTextures(1, &m_texture); } +#endif +#if HAVE_EGL + if (m_image != EGL_NO_IMAGE_KHR) { + ((eglDestroyImageKHR_func)(m_eglDestroyImageKHR))(eglGetCurrentDisplay(), m_image); + m_image = EGL_NO_IMAGE_KHR; + glDeleteTextures(1, &m_texture); + } #endif if (m_pixmap != XCB_WINDOW_NONE) { xcb_free_pixmap(QX11Info::connection(), m_pixmap); diff --git a/src/declarativeimports/core/windowthumbnail.h b/src/declarativeimports/core/windowthumbnail.h index 1d4595393..aa10e7ebe 100644 --- a/src/declarativeimports/core/windowthumbnail.h +++ b/src/declarativeimports/core/windowthumbnail.h @@ -26,8 +26,15 @@ // xcb #if HAVE_XCB_COMPOSITE #include -#endif +#if HAVE_EGL +#include +#include +#include // egl.h could include XLib.h + +#endif // HAVE_EGL + +#endif // HAVE_XCB_COMPOSITE class KWindowInfo; namespace Plasma { @@ -95,13 +102,24 @@ private: xcb_pixmap_t m_pixmap; uint m_texture; #if HAVE_GLX + bool windowToTextureGLX(WindowTextureNode *textureNode); void resolveGLXFunctions(); bool loadGLXTexture(); void bindGLXTexture(); QFunctionPointer m_bindTexImage; QFunctionPointer m_releaseTexImage; xcb_pixmap_t m_glxPixmap; -#endif +#endif // HAVE_GLX +#if HAVE_EGL + bool xcbWindowToTextureEGL(WindowTextureNode *textureNode); + void resolveEGLFunctions(); + void bindEGLTexture(); + bool m_eglFunctionsResolved; + QFunctionPointer m_eglCreateImageKHR; + QFunctionPointer m_eglDestroyImageKHR; + QFunctionPointer m_glEGLImageTargetTexture2DOES; + EGLImageKHR m_image; +#endif // HAVE_EGL #endif }; diff --git a/src/plasma/config-plasma.h.cmake b/src/plasma/config-plasma.h.cmake index c1aa75db7..1d59ae3ce 100644 --- a/src/plasma/config-plasma.h.cmake +++ b/src/plasma/config-plasma.h.cmake @@ -3,3 +3,4 @@ #cmakedefine01 PLASMA_NO_KUTILS #cmakedefine01 HAVE_X11 #cmakedefine01 HAVE_GLX +#cmakedefine01 HAVE_EGL