Commit f5b66a58 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

[plugins/qpa] Implement native offscreen surface

Summary:
Depending on whether the underlying platform supports offscreen surfaces,
QOffscreenSurface may create an invisible QWindow. In our case that's the
case, for each offscreen surface a native window is created. This may
lead to some funky results related to window decorations, see bug 407612.

There are several ways to implement offscreen surfaces - either use pbuffers
or utilize a surfaceless context extension. For the sake of simplicity
this change sticks with pbuffers, but it's a good idea to support both
methods.

CCBUG: 407612

Reviewers: #kwin, romangg

Reviewed By: #kwin, romangg

Subscribers: romangg, alexeymin, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D22150
parent 22d70d0c
......@@ -8,6 +8,7 @@ set(QPA_SOURCES
integration.cpp
main.cpp
nativeinterface.cpp
offscreensurface.cpp
platformcursor.cpp
screen.cpp
sharingplatformcontext.cpp
......
......@@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "platform.h"
#include "backingstore.h"
#include "nativeinterface.h"
#include "offscreensurface.h"
#include "screen.h"
#include "sharingplatformcontext.h"
#include "window.h"
......@@ -160,6 +161,11 @@ QPlatformWindow *Integration::createPlatformWindow(QWindow *window) const
}
}
QPlatformOffscreenSurface *Integration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
{
return new OffscreenSurface(surface);
}
QPlatformFontDatabase *Integration::fontDatabase() const
{
return m_fontDb;
......
......@@ -53,6 +53,7 @@ public:
bool hasCapability(Capability cap) const override;
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
QAbstractEventDispatcher *createEventDispatcher() const override;
QPlatformFontDatabase *fontDatabase() const override;
......
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "offscreensurface.h"
#include "eglhelpers.h"
#include "main.h"
#include "platform.h"
#include <QOffscreenSurface>
namespace KWin
{
namespace QPA
{
OffscreenSurface::OffscreenSurface(QOffscreenSurface *surface)
: QPlatformOffscreenSurface(surface)
, m_eglDisplay(kwinApp()->platform()->sceneEglDisplay())
{
const QSize size = surface->size();
EGLConfig config = configFromFormat(m_eglDisplay, surface->requestedFormat(), EGL_PBUFFER_BIT);
if (config == EGL_NO_CONFIG_KHR) {
return;
}
const EGLint attributes[] = {
EGL_WIDTH, size.width(),
EGL_HEIGHT, size.height(),
EGL_NONE
};
m_surface = eglCreatePbufferSurface(m_eglDisplay, config, attributes);
if (m_surface == EGL_NO_SURFACE) {
return;
}
// Requested and actual surface format might be different.
m_format = formatFromConfig(m_eglDisplay, config);
}
OffscreenSurface::~OffscreenSurface()
{
if (m_surface != EGL_NO_SURFACE) {
eglDestroySurface(m_eglDisplay, m_surface);
}
}
QSurfaceFormat OffscreenSurface::format() const
{
return m_format;
}
bool OffscreenSurface::isValid() const
{
return m_surface != EGL_NO_SURFACE;
}
EGLSurface OffscreenSurface::nativeHandle() const
{
return m_surface;
}
} // namespace QPA
} // namespace KWin
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#pragma once
#define MESA_EGL_NO_X11_HEADERS
#include <epoxy/egl.h>
#include "fixqopengl.h"
#include <qpa/qplatformoffscreensurface.h>
namespace KWin
{
namespace QPA
{
class OffscreenSurface : public QPlatformOffscreenSurface
{
public:
explicit OffscreenSurface(QOffscreenSurface *surface);
~OffscreenSurface() override;
QSurfaceFormat format() const override;
bool isValid() const override;
EGLSurface nativeHandle() const;
private:
QSurfaceFormat m_format;
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
EGLSurface m_surface = EGL_NO_SURFACE;
};
} // namespace QPA
} // namespace KWin
......@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "sharingplatformcontext.h"
#include "integration.h"
#include "offscreensurface.h"
#include "window.h"
#include "../../platform.h"
#include "../../wayland_server.h"
......@@ -48,22 +49,30 @@ SharingPlatformContext::SharingPlatformContext(QOpenGLContext *context, const EG
bool SharingPlatformContext::makeCurrent(QPlatformSurface *surface)
{
Window *window = static_cast<Window*>(surface);
EGLSurface eglSurface;
if (surface->surface()->surfaceClass() == QSurface::Window) {
eglSurface = m_surface;
} else {
eglSurface = static_cast<OffscreenSurface *>(surface)->nativeHandle();
}
// QOpenGLContext::makeCurrent in Qt5.12 calls platfrom->setContext before setCurrentContext
// but binding the content FBO looks up the format from the current context, so we need // to make sure sure Qt knows what the correct one is already
QOpenGLContextPrivate::setCurrentContext(context());
if (eglMakeCurrent(eglDisplay(), m_surface, m_surface, eglContext())) {
window->bindContentFBO();
return true;
const bool ok = eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext());
if (!ok) {
qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError());
return false;
}
qCWarning(KWIN_QPA) << "Failed to make context current";
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
qCWarning(KWIN_QPA) << "EGL error code: " << error;
if (surface->surface()->surfaceClass() == QSurface::Window) {
// QOpenGLContextPrivate::setCurrentContext will be called after this
// method returns, but that's too late, as we need a current context in
// order to bind the content framebuffer object.
QOpenGLContextPrivate::setCurrentContext(context());
Window *window = static_cast<Window *>(surface);
window->bindContentFBO();
}
return false;
return true;
}
bool SharingPlatformContext::isSharing() const
......@@ -73,16 +82,18 @@ bool SharingPlatformContext::isSharing() const
void SharingPlatformContext::swapBuffers(QPlatformSurface *surface)
{
Window *window = static_cast<Window*>(surface);
auto c = window->shellClient();
if (!c) {
qCDebug(KWIN_QPA) << "SwapBuffers called but there is no ShellClient";
return;
if (surface->surface()->surfaceClass() == QSurface::Window) {
Window *window = static_cast<Window *>(surface);
auto c = window->shellClient();
if (!c) {
qCDebug(KWIN_QPA) << "SwapBuffers called but there is no ShellClient";
return;
}
context()->makeCurrent(surface->surface());
glFlush();
c->setInternalFramebufferObject(window->swapFBO());
window->bindContentFBO();
}
context()->makeCurrent(surface->surface());
glFlush();
c->setInternalFramebufferObject(window->swapFBO());
window->bindContentFBO();
}
GLuint SharingPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const
......@@ -92,8 +103,9 @@ GLuint SharingPlatformContext::defaultFramebufferObject(QPlatformSurface *surfac
if (!fbo.isNull()) {
return fbo->handle();
}
qCDebug(KWIN_QPA) << "No default framebuffer object for internal window";
}
qCDebug(KWIN_QPA) << "No default framebuffer object for internal window";
return 0;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment