kwin/plugins/qpa/eglplatformcontext.cpp

348 lines
12 KiB
C++
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "eglplatformcontext.h"
#include "egl_context_attribute_builder.h"
#include "eglhelpers.h"
#include "internal_client.h"
#include "offscreensurface.h"
#include "platform.h"
#include "window.h"
#include "logging.h"
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <private/qopenglcontext_p.h>
#include <memory>
namespace KWin
{
namespace QPA
{
EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EGLDisplay display)
: m_eglDisplay(display)
{
create(context->format(), kwinApp()->platform()->sceneEglGlobalShareContext());
}
EGLPlatformContext::~EGLPlatformContext()
{
if (m_context != EGL_NO_CONTEXT) {
eglDestroyContext(m_eglDisplay, m_context);
}
}
EGLDisplay EGLPlatformContext::eglDisplay() const
{
return m_eglDisplay;
}
EGLContext EGLPlatformContext::eglContext() const
{
return m_context;
}
static EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface)
{
if (surface->surface()->surfaceClass() == QSurface::Window) {
return static_cast<Window *>(surface)->eglSurface();
} else {
return static_cast<OffscreenSurface *>(surface)->eglSurface();
}
}
bool EGLPlatformContext::makeCurrent(QPlatformSurface *surface)
{
const EGLSurface eglSurface = eglSurfaceForPlatformSurface(surface);
const bool ok = eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext());
if (!ok) {
qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError());
return false;
}
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 true;
}
void EGLPlatformContext::doneCurrent()
{
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
bool EGLPlatformContext::isValid() const
{
return m_context != EGL_NO_CONTEXT;
}
bool EGLPlatformContext::isSharing() const
{
return false;
}
QSurfaceFormat EGLPlatformContext::format() const
{
return m_format;
}
QFunctionPointer EGLPlatformContext::getProcAddress(const char *procName)
{
return eglGetProcAddress(procName);
}
void EGLPlatformContext::swapBuffers(QPlatformSurface *surface)
{
if (surface->surface()->surfaceClass() == QSurface::Window) {
Window *window = static_cast<Window *>(surface);
InternalClient *client = window->client();
if (!client) {
return;
}
context()->makeCurrent(surface->surface());
glFlush();
client->present(window->swapFBO());
window->bindContentFBO();
}
}
GLuint EGLPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
if (Window *window = dynamic_cast<Window *>(surface)) {
const auto &fbo = window->contentFBO();
if (!fbo.isNull()) {
return fbo->handle();
}
qCDebug(KWIN_QPA) << "No default framebuffer object for internal window";
}
return 0;
}
void EGLPlatformContext::create(const QSurfaceFormat &format, EGLContext shareContext)
{
if (!eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
qCWarning(KWIN_QPA, "eglBindAPI failed: 0x%x", eglGetError());
return;
}
m_config = configFromFormat(m_eglDisplay, format);
if (m_config == EGL_NO_CONFIG_KHR) {
qCWarning(KWIN_QPA) << "Could not find suitable EGLConfig for" << format;
return;
}
m_format = formatFromConfig(m_eglDisplay, m_config);
2015-11-13 08:45:11 +00:00
const QByteArray eglExtensions = eglQueryString(eglDisplay(), EGL_EXTENSIONS);
const QList<QByteArray> extensions = eglExtensions.split(' ');
const bool haveRobustness = extensions.contains(QByteArrayLiteral("EGL_EXT_create_context_robustness"));
const bool haveCreateContext = extensions.contains(QByteArrayLiteral("EGL_KHR_create_context"));
const bool haveContextPriority = extensions.contains(QByteArrayLiteral("EGL_IMG_context_priority"));
2015-11-13 08:45:11 +00:00
std::vector<std::unique_ptr<AbstractOpenGLContextAttributeBuilder>> candidates;
if (isOpenGLES()) {
if (haveCreateContext && haveRobustness && haveContextPriority) {
auto glesRobustPriority = std::make_unique<EglOpenGLESContextAttributeBuilder>();
glesRobustPriority->setVersion(2);
glesRobustPriority->setRobust(true);
glesRobustPriority->setHighPriority(true);
candidates.push_back(std::move(glesRobustPriority));
}
2015-11-13 08:45:11 +00:00
if (haveCreateContext && haveRobustness) {
auto glesRobust = std::make_unique<EglOpenGLESContextAttributeBuilder>();
glesRobust->setVersion(2);
glesRobust->setRobust(true);
candidates.push_back(std::move(glesRobust));
2015-11-13 08:45:11 +00:00
}
if (haveContextPriority) {
auto glesPriority = std::make_unique<EglOpenGLESContextAttributeBuilder>();
glesPriority->setVersion(2);
glesPriority->setHighPriority(true);
candidates.push_back(std::move(glesPriority));
}
auto gles = std::make_unique<EglOpenGLESContextAttributeBuilder>();
gles->setVersion(2);
candidates.push_back(std::move(gles));
} else {
// Try to create a 3.1 core context
2015-11-13 08:45:11 +00:00
if (m_format.majorVersion() >= 3 && haveCreateContext) {
if (haveRobustness && haveContextPriority) {
auto robustCorePriority = std::make_unique<EglContextAttributeBuilder>();
robustCorePriority->setVersion(m_format.majorVersion(), m_format.minorVersion());
robustCorePriority->setRobust(true);
robustCorePriority->setForwardCompatible(true);
if (m_format.profile() == QSurfaceFormat::CoreProfile) {
robustCorePriority->setCoreProfile(true);
} else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) {
robustCorePriority->setCompatibilityProfile(true);
}
robustCorePriority->setHighPriority(true);
candidates.push_back(std::move(robustCorePriority));
}
2015-11-13 08:45:11 +00:00
if (haveRobustness) {
auto robustCore = std::make_unique<EglContextAttributeBuilder>();
robustCore->setVersion(m_format.majorVersion(), m_format.minorVersion());
robustCore->setRobust(true);
robustCore->setForwardCompatible(true);
if (m_format.profile() == QSurfaceFormat::CoreProfile) {
robustCore->setCoreProfile(true);
} else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) {
robustCore->setCompatibilityProfile(true);
}
candidates.push_back(std::move(robustCore));
2015-11-13 08:45:11 +00:00
}
if (haveContextPriority) {
auto corePriority = std::make_unique<EglContextAttributeBuilder>();
corePriority->setVersion(m_format.majorVersion(), m_format.minorVersion());
corePriority->setForwardCompatible(true);
if (m_format.profile() == QSurfaceFormat::CoreProfile) {
corePriority->setCoreProfile(true);
} else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) {
corePriority->setCompatibilityProfile(true);
}
corePriority->setHighPriority(true);
candidates.push_back(std::move(corePriority));
}
auto core = std::make_unique<EglContextAttributeBuilder>();
core->setVersion(m_format.majorVersion(), m_format.minorVersion());
core->setForwardCompatible(true);
if (m_format.profile() == QSurfaceFormat::CoreProfile) {
core->setCoreProfile(true);
} else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) {
core->setCompatibilityProfile(true);
2015-11-13 08:45:11 +00:00
}
candidates.push_back(std::move(core));
}
if (haveRobustness && haveCreateContext && haveContextPriority) {
auto robustPriority = std::make_unique<EglContextAttributeBuilder>();
robustPriority->setRobust(true);
robustPriority->setHighPriority(true);
candidates.push_back(std::move(robustPriority));
}
if (haveRobustness && haveCreateContext) {
auto robust = std::make_unique<EglContextAttributeBuilder>();
robust->setRobust(true);
candidates.push_back(std::move(robust));
2015-11-13 08:45:11 +00:00
}
candidates.emplace_back(new EglContextAttributeBuilder);
}
EGLContext context = EGL_NO_CONTEXT;
for (auto it = candidates.begin(); it != candidates.end(); it++) {
const auto attribs = (*it)->build();
context = eglCreateContext(eglDisplay(), m_config, shareContext, attribs.data());
if (context != EGL_NO_CONTEXT) {
qCDebug(KWIN_QPA) << "Created EGL context with attributes:" << (*it).get();
break;
}
}
if (context == EGL_NO_CONTEXT) {
qCWarning(KWIN_QPA) << "Failed to create EGL context";
return;
}
m_context = context;
updateFormatFromContext();
}
static EGLSurface createDummyPbufferSurface(EGLDisplay display, const QSurfaceFormat &format)
{
const EGLConfig config = configFromFormat(display, format, EGL_PBUFFER_BIT);
if (config == EGL_NO_CONFIG_KHR) {
return EGL_NO_SURFACE;
}
const EGLint attribs[] = {
EGL_WIDTH, 16,
EGL_HEIGHT, 16,
EGL_NONE
};
return eglCreatePbufferSurface(display, config, attribs);
}
void EGLPlatformContext::updateFormatFromContext()
{
const EGLSurface oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
const EGLSurface oldReadSurface = eglGetCurrentSurface(EGL_READ);
const EGLContext oldContext = eglGetCurrentContext();
EGLSurface dummySurface;
if (!kwinApp()->platform()->supportsSurfacelessContext()) {
dummySurface = createDummyPbufferSurface(m_eglDisplay, m_format);
if (dummySurface == EGL_NO_SURFACE) {
qCWarning(KWIN_QPA, "Failed to create dummy surface: 0x%x", eglGetError());
return;
}
} else {
dummySurface = EGL_NO_SURFACE;
}
eglMakeCurrent(m_eglDisplay, dummySurface, dummySurface, m_context);
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
int major, minor;
if (parseOpenGLVersion(version, major, minor)) {
m_format.setMajorVersion(major);
m_format.setMinorVersion(minor);
} else {
qCWarning(KWIN_QPA) << "Unrecognized OpenGL version:" << version;
}
GLint value;
if (m_format.version() >= qMakePair(3, 0)) {
glGetIntegerv(GL_CONTEXT_FLAGS, &value);
if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
}
if (value & GL_CONTEXT_FLAG_DEBUG_BIT) {
m_format.setOption(QSurfaceFormat::DebugContext);
}
} else {
m_format.setOption(QSurfaceFormat::DeprecatedFunctions);
}
if (m_format.version() >= qMakePair(3, 2)) {
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
if (value & GL_CONTEXT_CORE_PROFILE_BIT) {
m_format.setProfile(QSurfaceFormat::CoreProfile);
} else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
} else {
m_format.setProfile(QSurfaceFormat::NoProfile);
}
} else {
m_format.setProfile(QSurfaceFormat::NoProfile);
}
eglMakeCurrent(m_eglDisplay, oldDrawSurface, oldReadSurface, oldContext);
if (dummySurface != EGL_NO_SURFACE) {
eglDestroySurface(m_eglDisplay, dummySurface);
}
}
} // namespace QPA
} // namespace KWin