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:
parent
710fe45527
commit
d2452e2917
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 // HAVE_GLX
|
||||||
|
|
||||||
|
#if HAVE_EGL
|
||||||
|
bool WindowThumbnail::xcbWindowToTextureEGL(WindowTextureNode *textureNode)
|
||||||
{
|
{
|
||||||
#endif
|
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);
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user