2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2015-08-14 14:52:40 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
2020-10-12 06:27:27 +00:00
|
|
|
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2015-08-14 14:52:40 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2020-10-12 06:27:27 +00:00
|
|
|
|
|
|
|
#include "eglplatformcontext.h"
|
2017-07-16 15:31:09 +00:00
|
|
|
#include "egl_context_attribute_builder.h"
|
2019-07-01 18:33:04 +00:00
|
|
|
#include "eglhelpers.h"
|
2020-10-12 06:27:27 +00:00
|
|
|
#include "internal_client.h"
|
|
|
|
#include "offscreensurface.h"
|
|
|
|
#include "platform.h"
|
|
|
|
#include "window.h"
|
2019-07-01 18:33:04 +00:00
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
#include "logging.h"
|
2015-08-14 14:52:40 +00:00
|
|
|
|
2019-07-01 18:33:04 +00:00
|
|
|
#include <QOpenGLContext>
|
2020-10-12 06:27:27 +00:00
|
|
|
#include <QOpenGLFramebufferObject>
|
|
|
|
|
|
|
|
#include <private/qopenglcontext_p.h>
|
2019-07-01 18:33:04 +00:00
|
|
|
|
2017-07-16 15:31:09 +00:00
|
|
|
#include <memory>
|
|
|
|
|
2015-08-14 14:52:40 +00:00
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
namespace QPA
|
|
|
|
{
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EGLDisplay display)
|
|
|
|
: m_eglDisplay(display)
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
2020-10-12 06:27:27 +00:00
|
|
|
create(context->format(), kwinApp()->platform()->sceneEglContext());
|
2015-08-14 14:52:40 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
EGLPlatformContext::~EGLPlatformContext()
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
|
|
|
if (m_context != EGL_NO_CONTEXT) {
|
|
|
|
eglDestroyContext(m_eglDisplay, m_context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
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()
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
|
|
|
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
bool EGLPlatformContext::isValid() const
|
|
|
|
{
|
|
|
|
return m_context != EGL_NO_CONTEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EGLPlatformContext::isSharing() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSurfaceFormat EGLPlatformContext::format() const
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
|
|
|
return m_format;
|
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
QFunctionPointer EGLPlatformContext::getProcAddress(const char *procName)
|
2016-03-09 12:42:24 +00:00
|
|
|
{
|
|
|
|
return eglGetProcAddress(procName);
|
|
|
|
}
|
2015-08-14 14:52:40 +00:00
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
void EGLPlatformContext::swapBuffers(QPlatformSurface *surface)
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
2020-10-12 06:27:27 +00:00
|
|
|
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();
|
|
|
|
}
|
2015-08-14 14:52:40 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
GLuint EGLPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
2020-10-12 06:27:27 +00:00
|
|
|
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";
|
2015-08-14 14:52:40 +00:00
|
|
|
}
|
2020-10-12 06:27:27 +00:00
|
|
|
|
|
|
|
return 0;
|
2015-08-14 14:52:40 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
void EGLPlatformContext::create(const QSurfaceFormat &format, EGLContext shareContext)
|
2015-08-14 14:52:40 +00:00
|
|
|
{
|
2020-10-12 06:27:27 +00:00
|
|
|
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"));
|
2018-03-22 09:20:37 +00:00
|
|
|
const bool haveContextPriority = extensions.contains(QByteArrayLiteral("EGL_IMG_context_priority"));
|
2015-11-13 08:45:11 +00:00
|
|
|
|
2017-07-16 15:31:09 +00:00
|
|
|
std::vector<std::unique_ptr<AbstractOpenGLContextAttributeBuilder>> candidates;
|
2015-10-30 11:42:59 +00:00
|
|
|
if (isOpenGLES()) {
|
2018-03-22 09:20:37 +00:00
|
|
|
if (haveCreateContext && haveRobustness && haveContextPriority) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto glesRobustPriority = std::make_unique<EglOpenGLESContextAttributeBuilder>();
|
2018-03-22 09:20:37 +00:00
|
|
|
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) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto glesRobust = std::make_unique<EglOpenGLESContextAttributeBuilder>();
|
2017-07-16 15:31:09 +00:00
|
|
|
glesRobust->setVersion(2);
|
|
|
|
glesRobust->setRobust(true);
|
|
|
|
candidates.push_back(std::move(glesRobust));
|
2015-11-13 08:45:11 +00:00
|
|
|
}
|
2018-03-22 09:20:37 +00:00
|
|
|
if (haveContextPriority) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto glesPriority = std::make_unique<EglOpenGLESContextAttributeBuilder>();
|
2018-03-22 09:20:37 +00:00
|
|
|
glesPriority->setVersion(2);
|
|
|
|
glesPriority->setHighPriority(true);
|
|
|
|
candidates.push_back(std::move(glesPriority));
|
|
|
|
}
|
2020-10-12 05:44:44 +00:00
|
|
|
auto gles = std::make_unique<EglOpenGLESContextAttributeBuilder>();
|
2017-07-16 15:31:09 +00:00
|
|
|
gles->setVersion(2);
|
|
|
|
candidates.push_back(std::move(gles));
|
2015-10-30 11:42:59 +00:00
|
|
|
} else {
|
|
|
|
// Try to create a 3.1 core context
|
2015-11-13 08:45:11 +00:00
|
|
|
if (m_format.majorVersion() >= 3 && haveCreateContext) {
|
2018-03-22 09:20:37 +00:00
|
|
|
if (haveRobustness && haveContextPriority) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto robustCorePriority = std::make_unique<EglContextAttributeBuilder>();
|
2018-03-22 09:20:37 +00:00
|
|
|
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) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto robustCore = std::make_unique<EglContextAttributeBuilder>();
|
2017-07-16 15:31:09 +00:00
|
|
|
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
|
|
|
}
|
2018-03-22 09:20:37 +00:00
|
|
|
if (haveContextPriority) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto corePriority = std::make_unique<EglContextAttributeBuilder>();
|
2018-03-22 09:20:37 +00:00
|
|
|
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));
|
|
|
|
}
|
2020-10-12 05:44:44 +00:00
|
|
|
auto core = std::make_unique<EglContextAttributeBuilder>();
|
2017-07-16 15:31:09 +00:00
|
|
|
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
|
|
|
}
|
2017-07-16 15:31:09 +00:00
|
|
|
candidates.push_back(std::move(core));
|
2015-10-30 11:42:59 +00:00
|
|
|
}
|
2018-03-22 09:20:37 +00:00
|
|
|
if (haveRobustness && haveCreateContext && haveContextPriority) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto robustPriority = std::make_unique<EglContextAttributeBuilder>();
|
2018-03-22 09:20:37 +00:00
|
|
|
robustPriority->setRobust(true);
|
|
|
|
robustPriority->setHighPriority(true);
|
|
|
|
candidates.push_back(std::move(robustPriority));
|
|
|
|
}
|
2017-07-16 15:31:09 +00:00
|
|
|
if (haveRobustness && haveCreateContext) {
|
2020-10-12 05:44:44 +00:00
|
|
|
auto robust = std::make_unique<EglContextAttributeBuilder>();
|
2017-07-16 15:31:09 +00:00
|
|
|
robust->setRobust(true);
|
|
|
|
candidates.push_back(std::move(robust));
|
2015-11-13 08:45:11 +00:00
|
|
|
}
|
2017-07-16 15:31:09 +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();
|
2020-10-12 06:27:27 +00:00
|
|
|
context = eglCreateContext(eglDisplay(), m_config, shareContext, attribs.data());
|
2017-07-16 15:31:09 +00:00
|
|
|
if (context != EGL_NO_CONTEXT) {
|
|
|
|
qCDebug(KWIN_QPA) << "Created EGL context with attributes:" << (*it).get();
|
|
|
|
break;
|
2015-10-30 11:42:59 +00:00
|
|
|
}
|
2015-08-14 14:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (context == EGL_NO_CONTEXT) {
|
2016-07-18 14:34:03 +00:00
|
|
|
qCWarning(KWIN_QPA) << "Failed to create EGL context";
|
2015-08-14 14:52:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_context = context;
|
|
|
|
}
|
|
|
|
|
2020-10-12 06:27:27 +00:00
|
|
|
} // namespace QPA
|
|
|
|
} // namespace KWin
|