qpa: Create a pbuffer for internal windows

If the surfaceless context extension is unsupported by the underlying
platform, the QPA will use the EGLSurface of the first output to make
OpenGL contexts current.

If an internal window attempts to make an OpenGL context current while
compositing is being restarted, for example it's typically the case with
the composited outline visual, QPA will either try to make the context
current with a no longer valid EGLSurface for the first output or will
crash during the call to Platform::supportsSurfacelessContext(). The
latter needs more explanation. After the compositingToggled() signal has
been emitted, there is no scene and supportsSurfacelessContext() doesn't
handle this case.

In either case, we could return EGL_NO_SURFACE if compositing is being
restarted, but if the underlying platform doesn't support the surfaceless
context extension, then the composited outline will not be able to
delete used textures, framebuffer objects, etc.

This change addresses that problem by making sure that every platform
window has a pbuffer allocated in case the surfaceless context extension
is unsupported.
This commit is contained in:
Vlad Zahorodnii 2020-10-16 19:20:58 +03:00
parent 41d431de27
commit cc8cb8db9d
2 changed files with 48 additions and 4 deletions

View file

@ -8,6 +8,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "window.h"
#include "eglhelpers.h"
#include "platform.h"
#include "screens.h"
@ -26,13 +27,23 @@ static quint32 s_windowId = 0;
Window::Window(QWindow *window)
: QPlatformWindow(window)
, m_eglDisplay(kwinApp()->platform()->sceneEglDisplay())
, m_windowId(++s_windowId)
, m_scale(screens()->maxScale())
{
if (window->surfaceType() == QSurface::OpenGLSurface) {
// The window will use OpenGL for drawing.
if (!kwinApp()->platform()->supportsSurfacelessContext()) {
createPbuffer();
}
}
}
Window::~Window()
{
if (m_eglSurface != EGL_NO_SURFACE) {
eglDestroySurface(m_eglDisplay, m_eglSurface);
}
unmap();
}
@ -47,6 +58,11 @@ void Window::setVisible(bool visible)
QPlatformWindow::setVisible(visible);
}
QSurfaceFormat Window::format() const
{
return m_format;
}
void Window::setGeometry(const QRect &rect)
{
const QRect &oldRect = geometry();
@ -123,6 +139,32 @@ void Window::createFBO()
m_resized = false;
}
void Window::createPbuffer()
{
const QSurfaceFormat requestedFormat = window()->requestedFormat();
const EGLConfig config = configFromFormat(m_eglDisplay,
requestedFormat,
EGL_PBUFFER_BIT);
if (config == EGL_NO_CONFIG_KHR) {
qCWarning(KWIN_QPA) << "Could not find any EGL config for:" << requestedFormat;
return;
}
// The size doesn't matter as we render into a framebuffer object.
const EGLint attribs[] = {
EGL_WIDTH, 16,
EGL_HEIGHT, 16,
EGL_NONE
};
m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, config, attribs);
if (m_eglSurface != EGL_NO_SURFACE) {
m_format = formatFromConfig(m_eglDisplay, config);
} else {
qCWarning(KWIN_QPA, "Failed to create a pbuffer for window: 0x%x", eglGetError());
}
}
void Window::map()
{
if (m_handle) {
@ -146,10 +188,7 @@ void Window::unmap()
EGLSurface Window::eglSurface() const
{
if (kwinApp()->platform()->supportsSurfacelessContext()) {
return EGL_NO_SURFACE;
}
return kwinApp()->platform()->sceneEglSurface();
return m_eglSurface;
}
}

View file

@ -33,6 +33,7 @@ public:
explicit Window(QWindow *window);
~Window() override;
QSurfaceFormat format() const override;
void setVisible(bool visible) override;
void setGeometry(const QRect &rect) override;
WId winId() const override;
@ -47,11 +48,15 @@ public:
private:
void createFBO();
void createPbuffer();
void map();
void unmap();
QSurfaceFormat m_format;
QPointer<InternalClient> m_handle;
QSharedPointer<QOpenGLFramebufferObject> m_contentFBO;
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
EGLSurface m_eglSurface = EGL_NO_SURFACE;
quint32 m_windowId;
bool m_resized = false;
int m_scale = 1;