EGL/X11 backend for WindowThumbnail QQuickItem

Introduces a new optional dependency to EGL. If EGL is found the
WindowThumbnail gets compiled with EGL support. The EGL support is
unlike Qt's XCB plugin not mutual exclusive to the GLX backend, thus
it's possible to compile with EGL and GLX at the same time.
By that this implementation is prepared for the case that Qt supports
EGL or GLX through runtime selection.

In practice EGL support is only useful if Qt is compiled with GLESv2.
In that case the corebindingsplugin gets linked against Qt5::Gui_GLESv2
to get the dependency to GLES.

The implementation makes use of the EGL_KHR_image extension
(or EGL_KHR_image_base and EGL_KHR_Image_pixmap) and
the GL_OES_EGL_image extension to bind the X pixmap to an EGLImageKHR.

REVIEW: 116627
This commit is contained in:
Martin Gräßlin 2014-03-06 09:43:52 +01:00
parent 710fe45527
commit d2452e2917
5 changed files with 164 additions and 26 deletions

View File

@ -102,6 +102,14 @@ set_package_properties(OpenGL PROPERTIES DESCRIPTION "The OpenGL libraries"
URL "http://www.opengl.org" URL "http://www.opengl.org"
TYPE OPTIONAL 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")) if(OPENGL_FOUND AND (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL"))
set(HAVE_GLX ${HAVE_X11}) set(HAVE_GLX ${HAVE_X11})
else() else()

View File

@ -66,6 +66,14 @@ if(HAVE_X11)
endif() endif()
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(TARGETS corebindingsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core)
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core)

View File

@ -34,6 +34,11 @@ typedef void (*glXBindTexImageEXT_func)(Display* dpy, GLXDrawable drawable,
int buffer, const int* attrib_list); int buffer, const int* attrib_list);
typedef void (*glXReleaseTexImageEXT_func)(Display* dpy, GLXDrawable drawable, int buffer); typedef void (*glXReleaseTexImageEXT_func)(Display* dpy, GLXDrawable drawable, int buffer);
#endif #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 #endif
namespace Plasma { namespace Plasma {
@ -67,7 +72,11 @@ WindowThumbnail::WindowThumbnail(QQuickItem* parent)
, m_texture(0) , m_texture(0)
#if HAVE_GLX #if HAVE_GLX
, m_glxPixmap(XCB_PIXMAP_NONE) , 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 #endif
{ {
setFlag(ItemHasContents); setFlag(ItemHasContents);
@ -189,36 +198,23 @@ void WindowThumbnail::iconToTexture(WindowTextureNode *textureNode)
textureNode->reset(window()->createTextureFromImage(image)); textureNode->reset(window()->createTextureFromImage(image));
} }
void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode)
{
if (!m_damaged && textureNode->texture()) {
return;
}
#if HAVE_XCB_COMPOSITE #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 #if HAVE_GLX
bool WindowThumbnail::windowToTextureGLX(WindowTextureNode *textureNode)
{
if (glXGetCurrentContext()) { if (glXGetCurrentContext()) {
if (!m_openGLFunctionsResolved) { if (!m_openGLFunctionsResolved) {
resolveGLXFunctions(); resolveGLXFunctions();
} }
if (!m_bindTexImage || !m_releaseTexImage) { if (!m_bindTexImage || !m_releaseTexImage) {
iconToTexture(textureNode); return false;
return;
} }
if (m_glxPixmap == XCB_PIXMAP_NONE) { if (m_glxPixmap == XCB_PIXMAP_NONE) {
xcb_connection_t *c = QX11Info::connection();
auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap); auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap);
if (!loadGLXTexture()) { if (!loadGLXTexture()) {
iconToTexture(textureNode); return false;
return;
} }
QScopedPointer<xcb_get_geometry_reply_t, QScopedPointerPodDeleter> geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR)); QScopedPointer<xcb_get_geometry_reply_t, QScopedPointerPodDeleter> geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR));
@ -231,14 +227,114 @@ void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode)
} }
textureNode->texture()->bind(); textureNode->texture()->bind();
bindGLXTexture(); bindGLXTexture();
} else { return true;
#else }
{ return false;
#endif }
#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<xcb_get_geometry_reply_t, QScopedPointerPodDeleter> 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<QByteArray> 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 // just for safety to not crash
iconToTexture(textureNode); iconToTexture(textureNode);
} }
// TODO: add an egl variant
textureNode->markDirty(QSGNode::DirtyForceUpdate); textureNode->markDirty(QSGNode::DirtyForceUpdate);
#endif #endif
} }
@ -393,6 +489,13 @@ void WindowThumbnail::discardPixmap()
m_glxPixmap = XCB_PIXMAP_NONE; m_glxPixmap = XCB_PIXMAP_NONE;
glDeleteTextures(1, &m_texture); 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 #endif
if (m_pixmap != XCB_WINDOW_NONE) { if (m_pixmap != XCB_WINDOW_NONE) {
xcb_free_pixmap(QX11Info::connection(), m_pixmap); xcb_free_pixmap(QX11Info::connection(), m_pixmap);

View File

@ -26,8 +26,15 @@
// xcb // xcb
#if HAVE_XCB_COMPOSITE #if HAVE_XCB_COMPOSITE
#include <xcb/damage.h> #include <xcb/damage.h>
#endif
#if HAVE_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <fixx11h.h> // egl.h could include XLib.h
#endif // HAVE_EGL
#endif // HAVE_XCB_COMPOSITE
class KWindowInfo; class KWindowInfo;
namespace Plasma { namespace Plasma {
@ -95,13 +102,24 @@ private:
xcb_pixmap_t m_pixmap; xcb_pixmap_t m_pixmap;
uint m_texture; uint m_texture;
#if HAVE_GLX #if HAVE_GLX
bool windowToTextureGLX(WindowTextureNode *textureNode);
void resolveGLXFunctions(); void resolveGLXFunctions();
bool loadGLXTexture(); bool loadGLXTexture();
void bindGLXTexture(); void bindGLXTexture();
QFunctionPointer m_bindTexImage; QFunctionPointer m_bindTexImage;
QFunctionPointer m_releaseTexImage; QFunctionPointer m_releaseTexImage;
xcb_pixmap_t m_glxPixmap; 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 #endif
}; };

View File

@ -3,3 +3,4 @@
#cmakedefine01 PLASMA_NO_KUTILS #cmakedefine01 PLASMA_NO_KUTILS
#cmakedefine01 HAVE_X11 #cmakedefine01 HAVE_X11
#cmakedefine01 HAVE_GLX #cmakedefine01 HAVE_GLX
#cmakedefine01 HAVE_EGL