WindowThumbnail: Do GL calls in the correct thread
WindowThumbnail did some open GL operations, discarding old textures, in the GUI thread. Whislt it's not going to cause a threading issue (as updatePaintNode always ran when the main thread was blocked) we're not meant to mix threads with openGL contexts. It also seems to have a GL leak on nvidia, which was previously masked by the double delete fixed in https://git.reviewboard.kde.org/r/126131/diff/2/ It seems only one worked, and in the applied version we went with the wrong one. This patch makes use of QQuickItem::releaseResources to delete the GL textures on window change and destructor; it's then removed from stopRedirecting so that start/stop redirecting handles xcb on the GUI thread and updatePaintNode/discardPixmap is the GL stuff on the render thread. See http://doc.qt.io/qt-5/qquickitem.html#graphics-resource-handling REVIEW: 128763 BUG: 368066
This commit is contained in:
parent
eb9c0bbdf8
commit
8e517b1578
@ -24,6 +24,8 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <QRunnable>
|
||||||
|
|
||||||
// X11
|
// X11
|
||||||
#if HAVE_XCB_COMPOSITE
|
#if HAVE_XCB_COMPOSITE
|
||||||
#include <QX11Info>
|
#include <QX11Info>
|
||||||
@ -44,6 +46,72 @@ typedef GLvoid(*glEGLImageTargetTexture2DOES_func)(GLenum, GLeglImageOES);
|
|||||||
namespace Plasma
|
namespace Plasma
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if HAVE_XCB_COMPOSITE
|
||||||
|
#if HAVE_GLX
|
||||||
|
class DiscardGlxPixmapRunnable : public QRunnable {
|
||||||
|
public:
|
||||||
|
DiscardGlxPixmapRunnable(
|
||||||
|
uint,
|
||||||
|
QFunctionPointer,
|
||||||
|
xcb_pixmap_t
|
||||||
|
);
|
||||||
|
void run() Q_DECL_OVERRIDE;
|
||||||
|
private:
|
||||||
|
uint m_texture;
|
||||||
|
QFunctionPointer m_releaseTexImage;
|
||||||
|
xcb_pixmap_t m_glxPixmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
DiscardGlxPixmapRunnable::DiscardGlxPixmapRunnable(uint texture, QFunctionPointer deleteFunction, xcb_pixmap_t pixmap)
|
||||||
|
: QRunnable(),
|
||||||
|
m_texture(texture),
|
||||||
|
m_releaseTexImage(deleteFunction),
|
||||||
|
m_glxPixmap(pixmap)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DiscardGlxPixmapRunnable::run()
|
||||||
|
{
|
||||||
|
if (m_glxPixmap != XCB_PIXMAP_NONE) {
|
||||||
|
Display *d = QX11Info::display();
|
||||||
|
((glXReleaseTexImageEXT_func)(m_releaseTexImage))(d, m_glxPixmap, GLX_FRONT_LEFT_EXT);
|
||||||
|
glXDestroyPixmap(d, m_glxPixmap);
|
||||||
|
glDeleteTextures(1, &m_texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //HAVE_GLX
|
||||||
|
|
||||||
|
#if HAVE_EGL
|
||||||
|
class DiscardEglPixmapRunnable : public QRunnable {
|
||||||
|
public:
|
||||||
|
DiscardEglPixmapRunnable(
|
||||||
|
uint,
|
||||||
|
QFunctionPointer,
|
||||||
|
EGLImageKHR
|
||||||
|
);
|
||||||
|
void run() Q_DECL_OVERRIDE;
|
||||||
|
private:
|
||||||
|
uint m_texture;
|
||||||
|
QFunctionPointer m_eglDestroyImageKHR;
|
||||||
|
EGLImageKHR m_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
DiscardEglPixmapRunnable::DiscardEglPixmapRunnable(uint texture, QFunctionPointer deleteFunction, EGLImageKHR image)
|
||||||
|
: QRunnable(),
|
||||||
|
m_texture(texture),
|
||||||
|
m_eglDestroyImageKHR(deleteFunction),
|
||||||
|
m_image(image)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DiscardEglPixmapRunnable::run()
|
||||||
|
{
|
||||||
|
if (m_image != EGL_NO_IMAGE_KHR) {
|
||||||
|
((eglDestroyImageKHR_func)(m_eglDestroyImageKHR))(eglGetCurrentDisplay(), m_image);
|
||||||
|
glDeleteTextures(1, &m_texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif//HAVE_EGL
|
||||||
|
#endif //HAVE_XCB_COMPOSITE
|
||||||
|
|
||||||
WindowTextureNode::WindowTextureNode()
|
WindowTextureNode::WindowTextureNode()
|
||||||
: QSGSimpleTextureNode()
|
: QSGSimpleTextureNode()
|
||||||
{
|
{
|
||||||
@ -101,6 +169,7 @@ WindowThumbnail::WindowThumbnail(QQuickItem *parent)
|
|||||||
connect(this, &QQuickItem::enabledChanged, [this]() {
|
connect(this, &QQuickItem::enabledChanged, [this]() {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
stopRedirecting();
|
stopRedirecting();
|
||||||
|
releaseResources();
|
||||||
} else if (isVisible()) {
|
} else if (isVisible()) {
|
||||||
startRedirecting();
|
startRedirecting();
|
||||||
}
|
}
|
||||||
@ -108,6 +177,7 @@ WindowThumbnail::WindowThumbnail(QQuickItem *parent)
|
|||||||
connect(this, &QQuickItem::visibleChanged, [this]() {
|
connect(this, &QQuickItem::visibleChanged, [this]() {
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
stopRedirecting();
|
stopRedirecting();
|
||||||
|
releaseResources();
|
||||||
} else if (isEnabled()) {
|
} else if (isEnabled()) {
|
||||||
startRedirecting();
|
startRedirecting();
|
||||||
}
|
}
|
||||||
@ -138,10 +208,48 @@ WindowThumbnail::~WindowThumbnail()
|
|||||||
if (m_xcb) {
|
if (m_xcb) {
|
||||||
QCoreApplication::instance()->removeNativeEventFilter(this);
|
QCoreApplication::instance()->removeNativeEventFilter(this);
|
||||||
stopRedirecting();
|
stopRedirecting();
|
||||||
discardPixmap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowThumbnail::releaseResources()
|
||||||
|
{
|
||||||
|
#if HAVE_XCB_COMPOSITE
|
||||||
|
|
||||||
|
#if HAVE_GLX && HAVE_EGL
|
||||||
|
//only one (or none) should be set, but never both
|
||||||
|
Q_ASSERT(m_glxPixmap == XCB_PIXMAP_NONE || m_image == EGL_NO_IMAGE_KHR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//data is deleted in the render thread (with relevant GLX calls)
|
||||||
|
//note runnable may be called *after* this is deleted
|
||||||
|
//but the pointer is held by the WindowThumbnail which is in the main thread
|
||||||
|
#if HAVE_GLX
|
||||||
|
if (m_glxPixmap != XCB_PIXMAP_NONE) {
|
||||||
|
window()->scheduleRenderJob(new DiscardGlxPixmapRunnable(m_texture,
|
||||||
|
m_releaseTexImage,
|
||||||
|
m_glxPixmap),
|
||||||
|
QQuickWindow::NoStage);
|
||||||
|
|
||||||
|
m_glxPixmap = XCB_PIXMAP_NONE;
|
||||||
|
m_texture = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if HAVE_EGL
|
||||||
|
if (m_image != EGL_NO_IMAGE_KHR) {
|
||||||
|
window()->scheduleRenderJob(new DiscardEglPixmapRunnable(m_texture,
|
||||||
|
m_eglDestroyImageKHR,
|
||||||
|
m_image),
|
||||||
|
QQuickWindow::NoStage);
|
||||||
|
|
||||||
|
m_image = EGL_NO_IMAGE_KHR;
|
||||||
|
m_texture = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t WindowThumbnail::winId() const
|
uint32_t WindowThumbnail::winId() const
|
||||||
{
|
{
|
||||||
return m_winId;
|
return m_winId;
|
||||||
@ -227,8 +335,9 @@ bool WindowThumbnail::nativeEventFilter(const QByteArray &eventType, void *messa
|
|||||||
}
|
}
|
||||||
} else if (responseType == XCB_CONFIGURE_NOTIFY) {
|
} else if (responseType == XCB_CONFIGURE_NOTIFY) {
|
||||||
if (reinterpret_cast<xcb_configure_notify_event_t *>(event)->window == m_winId) {
|
if (reinterpret_cast<xcb_configure_notify_event_t *>(event)->window == m_winId) {
|
||||||
// TODO: only discard if size changes
|
releaseResources();
|
||||||
discardPixmap();
|
m_damaged = true;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -373,16 +482,7 @@ void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode)
|
|||||||
if (!textureNode->texture()) {
|
if (!textureNode->texture()) {
|
||||||
// the texture got discarded by the scene graph, but our mapping is still valid
|
// the texture got discarded by the scene graph, but our mapping is still valid
|
||||||
// let's discard the pixmap to have a clean state again
|
// let's discard the pixmap to have a clean state again
|
||||||
#if HAVE_GLX
|
releaseResources();
|
||||||
if (m_glxPixmap != XCB_PIXMAP_NONE) {
|
|
||||||
discardPixmap();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if HAVE_EGL
|
|
||||||
if (m_image != EGL_NO_IMAGE_KHR) {
|
|
||||||
discardPixmap();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (m_pixmap == XCB_PIXMAP_NONE) {
|
if (m_pixmap == XCB_PIXMAP_NONE) {
|
||||||
m_pixmap = pixmapForWindow();
|
m_pixmap = pixmapForWindow();
|
||||||
@ -592,7 +692,6 @@ void WindowThumbnail::stopRedirecting()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
xcb_composite_unredirect_window(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
|
xcb_composite_unredirect_window(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
|
||||||
discardPixmap();
|
|
||||||
if (m_damage == XCB_NONE) {
|
if (m_damage == XCB_NONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -635,36 +734,7 @@ void WindowThumbnail::startRedirecting()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowThumbnail::discardPixmap()
|
|
||||||
{
|
|
||||||
if (!m_xcb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if HAVE_XCB_COMPOSITE
|
|
||||||
#if HAVE_GLX
|
|
||||||
if (m_glxPixmap != XCB_PIXMAP_NONE) {
|
|
||||||
Display *d = QX11Info::display();
|
|
||||||
((glXReleaseTexImageEXT_func)(m_releaseTexImage))(d, m_glxPixmap, GLX_FRONT_LEFT_EXT);
|
|
||||||
glXDestroyPixmap(d, m_glxPixmap);
|
|
||||||
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);
|
|
||||||
m_pixmap = XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
m_damaged = true;
|
|
||||||
update();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowThumbnail::setThumbnailAvailable(bool thumbnailAvailable)
|
void WindowThumbnail::setThumbnailAvailable(bool thumbnailAvailable)
|
||||||
{
|
{
|
||||||
|
@ -99,13 +99,15 @@ Q_SIGNALS:
|
|||||||
void paintedSizeChanged();
|
void paintedSizeChanged();
|
||||||
void thumbnailAvailableChanged();
|
void thumbnailAvailableChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void releaseResources() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void iconToTexture(WindowTextureNode *textureNode);
|
void iconToTexture(WindowTextureNode *textureNode);
|
||||||
void windowToTexture(WindowTextureNode *textureNode);
|
void windowToTexture(WindowTextureNode *textureNode);
|
||||||
void startRedirecting();
|
void startRedirecting();
|
||||||
void stopRedirecting();
|
void stopRedirecting();
|
||||||
void resetDamaged();
|
void resetDamaged();
|
||||||
void discardPixmap();
|
|
||||||
void setThumbnailAvailable(bool thumbnailAvailable);
|
void setThumbnailAvailable(bool thumbnailAvailable);
|
||||||
|
|
||||||
bool m_xcb;
|
bool m_xcb;
|
||||||
@ -121,6 +123,8 @@ private:
|
|||||||
uint8_t m_damageEventBase;
|
uint8_t m_damageEventBase;
|
||||||
xcb_damage_damage_t m_damage;
|
xcb_damage_damage_t m_damage;
|
||||||
xcb_pixmap_t m_pixmap;
|
xcb_pixmap_t m_pixmap;
|
||||||
|
|
||||||
|
/*The following must *only* be used from the render thread*/
|
||||||
uint m_texture;
|
uint m_texture;
|
||||||
#if HAVE_GLX
|
#if HAVE_GLX
|
||||||
bool windowToTextureGLX(WindowTextureNode *textureNode);
|
bool windowToTextureGLX(WindowTextureNode *textureNode);
|
||||||
|
Loading…
Reference in New Issue
Block a user