Introduce a shared base class for the EGL backends
There's quite some overlap and duplicated code. This AbstractEglBackend tries to merge the two backends a little bit again. This also introduces an AbstractEglTexture which supports both X11 and Wayland "pixmaps" so that EglOnXBackend can support Wayland buffers.icc-effect-5.14.5
parent
a1642a85d3
commit
dae8eed3a8
|
@ -415,7 +415,7 @@ if(KWIN_BUILD_ACTIVITIES)
|
|||
endif()
|
||||
|
||||
if(KWIN_HAVE_EGL)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} eglonxbackend.cpp)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} abstract_egl_backend.cpp eglonxbackend.cpp)
|
||||
endif()
|
||||
|
||||
if(HAVE_WAYLAND)
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 "abstract_egl_backend.h"
|
||||
#include "options.h"
|
||||
#if HAVE_WAYLAND
|
||||
#include "wayland_server.h"
|
||||
#include <KWayland/Server/buffer_interface.h>
|
||||
#include <KWayland/Server/display.h>
|
||||
#endif
|
||||
// kwin libs
|
||||
#include <kwinglplatform.h>
|
||||
// Qt
|
||||
#include <QOpenGLContext>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
#if HAVE_WAYLAND
|
||||
typedef GLboolean(*eglBindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display);
|
||||
typedef GLboolean(*eglUnbindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display);
|
||||
typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
|
||||
eglBindWaylandDisplayWL_func eglBindWaylandDisplayWL = nullptr;
|
||||
eglUnbindWaylandDisplayWL_func eglUnbindWaylandDisplayWL = nullptr;
|
||||
eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
|
||||
|
||||
#ifndef EGL_WAYLAND_BUFFER_WL
|
||||
#define EGL_WAYLAND_BUFFER_WL 0x31D5
|
||||
#endif
|
||||
#ifndef EGL_WAYLAND_PLANE_WL
|
||||
#define EGL_WAYLAND_PLANE_WL 0x31D6
|
||||
#endif
|
||||
#ifndef EGL_WAYLAND_Y_INVERTED_WL
|
||||
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
|
||||
#endif
|
||||
#endif
|
||||
|
||||
AbstractEglBackend::AbstractEglBackend()
|
||||
: OpenGLBackend()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractEglBackend::~AbstractEglBackend() = default;
|
||||
|
||||
void AbstractEglBackend::cleanup()
|
||||
{
|
||||
#if HAVE_WAYLAND
|
||||
if (eglUnbindWaylandDisplayWL && eglDisplay() != EGL_NO_DISPLAY) {
|
||||
eglUnbindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display()));
|
||||
}
|
||||
#endif
|
||||
cleanupGL();
|
||||
doneCurrent();
|
||||
eglDestroyContext(m_display, m_context);
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
eglTerminate(m_display);
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
bool AbstractEglBackend::initEglAPI()
|
||||
{
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(m_display, &major, &minor) == EGL_FALSE) {
|
||||
return false;
|
||||
}
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
qCWarning(KWIN_CORE) << "Error during eglInitialize " << error;
|
||||
return false;
|
||||
}
|
||||
qCDebug(KWIN_CORE) << "Egl Initialize succeeded";
|
||||
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
#else
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "bind OpenGL API failed";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
qCDebug(KWIN_CORE) << "EGL version: " << major << "." << minor;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractEglBackend::initKWinGL()
|
||||
{
|
||||
initEGL();
|
||||
GLPlatform *glPlatform = GLPlatform::instance();
|
||||
glPlatform->detect(EglPlatformInterface);
|
||||
if (GLPlatform::instance()->driver() == Driver_Intel)
|
||||
options->setUnredirectFullscreen(false); // bug #252817
|
||||
options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting
|
||||
if (options->glPreferBufferSwap() == Options::AutoSwapStrategy)
|
||||
options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen
|
||||
glPlatform->printResults();
|
||||
initGL(EglPlatformInterface);
|
||||
}
|
||||
|
||||
void AbstractEglBackend::initBufferAge()
|
||||
{
|
||||
setSupportsBufferAge(false);
|
||||
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_EXT_buffer_age"))) {
|
||||
const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
|
||||
|
||||
if (useBufferAge != "0")
|
||||
setSupportsBufferAge(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractEglBackend::initWayland()
|
||||
{
|
||||
#if HAVE_WAYLAND
|
||||
if (!WaylandServer::self()) {
|
||||
return;
|
||||
}
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_WL_bind_wayland_display"))) {
|
||||
eglBindWaylandDisplayWL = (eglBindWaylandDisplayWL_func)eglGetProcAddress("eglBindWaylandDisplayWL");
|
||||
eglUnbindWaylandDisplayWL = (eglUnbindWaylandDisplayWL_func)eglGetProcAddress("eglUnbindWaylandDisplayWL");
|
||||
eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
|
||||
if (!eglBindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display()))) {
|
||||
eglUnbindWaylandDisplayWL = nullptr;
|
||||
eglQueryWaylandBufferWL = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AbstractEglBackend::initClientExtensions()
|
||||
{
|
||||
// Get the list of client extensions
|
||||
const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
|
||||
if (clientExtensionsString.isEmpty()) {
|
||||
// If eglQueryString() returned NULL, the implementation doesn't support
|
||||
// EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
|
||||
(void) eglGetError();
|
||||
}
|
||||
|
||||
m_clientExtensions = clientExtensionsString.split(' ');
|
||||
}
|
||||
|
||||
bool AbstractEglBackend::hasClientExtension(const QByteArray &ext) const
|
||||
{
|
||||
return m_clientExtensions.contains(ext);
|
||||
}
|
||||
|
||||
bool AbstractEglBackend::makeCurrent()
|
||||
{
|
||||
if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
|
||||
// Workaround to tell Qt that no QOpenGLContext is current
|
||||
context->doneCurrent();
|
||||
}
|
||||
const bool current = eglMakeCurrent(m_display, m_surface, m_surface, m_context);
|
||||
return current;
|
||||
}
|
||||
|
||||
void AbstractEglBackend::doneCurrent()
|
||||
{
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
AbstractEglTexture::AbstractEglTexture(SceneOpenGL::Texture *texture, AbstractEglBackend *backend)
|
||||
: SceneOpenGL::TexturePrivate()
|
||||
, q(texture)
|
||||
, m_backend(backend)
|
||||
{
|
||||
m_target = GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
AbstractEglTexture::~AbstractEglTexture()
|
||||
{
|
||||
if (m_image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(m_backend->eglDisplay(), m_image);
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLBackend *AbstractEglTexture::backend()
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
bool AbstractEglTexture::loadTexture(WindowPixmap *pixmap)
|
||||
{
|
||||
#if HAVE_WAYLAND
|
||||
const auto &buffer = pixmap->buffer();
|
||||
if (buffer.isNull()) {
|
||||
// try X11 loading
|
||||
return loadTexture(pixmap->pixmap(), pixmap->toplevel()->size());
|
||||
}
|
||||
// try Wayland loading
|
||||
if (buffer->shmBuffer()) {
|
||||
return loadShmTexture(buffer);
|
||||
} else {
|
||||
return loadEglTexture(buffer);
|
||||
}
|
||||
#else
|
||||
return loadTexture(pixmap->pixmap(), pixmap->toplevel()->size());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AbstractEglTexture::loadTexture(xcb_pixmap_t pix, const QSize &size)
|
||||
{
|
||||
if (pix == XCB_NONE)
|
||||
return false;
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
const EGLint attribs[] = {
|
||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||
EGL_NONE
|
||||
};
|
||||
m_image = eglCreateImageKHR(m_backend->eglDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
||||
(EGLClientBuffer)pix, attribs);
|
||||
|
||||
if (EGL_NO_IMAGE_KHR == m_image) {
|
||||
qCDebug(KWIN_CORE) << "failed to create egl image";
|
||||
q->unbind();
|
||||
q->discard();
|
||||
return false;
|
||||
}
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)m_image);
|
||||
q->unbind();
|
||||
q->setYInverted(true);
|
||||
m_size = size;
|
||||
updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractEglTexture::updateTexture(WindowPixmap *pixmap)
|
||||
{
|
||||
const auto &buffer = pixmap->buffer();
|
||||
if (buffer.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!buffer->shmBuffer()) {
|
||||
q->bind();
|
||||
EGLImageKHR image = attach(buffer);
|
||||
q->unbind();
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(m_backend->eglDisplay(), m_image);
|
||||
m_image = image;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// shm fallback
|
||||
if (GLPlatform::instance()->isGLES()) {
|
||||
// FIXME
|
||||
return;
|
||||
}
|
||||
const QImage &image = buffer->data();
|
||||
if (image.isNull()) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(image.size() == m_size);
|
||||
q->bind();
|
||||
const QRegion &damage = pixmap->toplevel()->damage();
|
||||
|
||||
// TODO: this should be shared with GLTexture::update
|
||||
const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
for (const QRect &rect : damage.rects()) {
|
||||
glTexSubImage2D(m_target, 0, rect.x(), rect.y(), rect.width(), rect.height(),
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, im.copy(rect).bits());
|
||||
}
|
||||
q->unbind();
|
||||
}
|
||||
|
||||
#if HAVE_WAYLAND
|
||||
bool AbstractEglTexture::loadShmTexture(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
if (GLPlatform::instance()->isGLES()) {
|
||||
// FIXME
|
||||
return false;
|
||||
}
|
||||
const QImage &image = buffer->data();
|
||||
if (image.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
|
||||
const QSize &size = image.size();
|
||||
// TODO: this should be shared with GLTexture(const QImage&, GLenum)
|
||||
GLenum format = 0;
|
||||
switch (image.format()) {
|
||||
case QImage::Format_ARGB32:
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
format = GL_RGBA8;
|
||||
break;
|
||||
case QImage::Format_RGB32:
|
||||
format = GL_RGB8;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
glTexImage2D(m_target, 0, format, size.width(), size.height(), 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
|
||||
|
||||
q->unbind();
|
||||
q->setYInverted(true);
|
||||
m_size = size;
|
||||
updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractEglTexture::loadEglTexture(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
if (!eglQueryWaylandBufferWL) {
|
||||
return false;
|
||||
}
|
||||
if (!buffer->resource()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
m_image = attach(buffer);
|
||||
q->unbind();
|
||||
|
||||
if (EGL_NO_IMAGE_KHR == m_image) {
|
||||
qCDebug(KWIN_CORE) << "failed to create egl image";
|
||||
q->discard();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EGLImageKHR AbstractEglTexture::attach(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
EGLint format, width, height, yInverted;
|
||||
eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_TEXTURE_FORMAT, &format);
|
||||
if (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA) {
|
||||
qCDebug(KWIN_CORE) << "Unsupported texture format: " << format;
|
||||
return EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_WAYLAND_Y_INVERTED_WL, &yInverted);
|
||||
eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_WIDTH, &width);
|
||||
eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_HEIGHT, &height);
|
||||
|
||||
const EGLint attribs[] = {
|
||||
EGL_WAYLAND_PLANE_WL, 0,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLImageKHR image = eglCreateImageKHR(m_backend->eglDisplay(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL,
|
||||
(EGLClientBuffer)buffer->resource(), attribs);
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
|
||||
m_size = QSize(width, height);
|
||||
updateMatrix();
|
||||
q->setYInverted(yInverted);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_ABSTRACT_EGL_BACKEND_H
|
||||
#define KWIN_ABSTRACT_EGL_BACKEND_H
|
||||
#include "scene_opengl.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class AbstractEglBackend : public OpenGLBackend
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractEglBackend();
|
||||
bool makeCurrent() override;
|
||||
void doneCurrent() override;
|
||||
|
||||
EGLDisplay eglDisplay() const {
|
||||
return m_display;
|
||||
}
|
||||
|
||||
protected:
|
||||
AbstractEglBackend();
|
||||
EGLContext context() const {
|
||||
return m_context;
|
||||
}
|
||||
EGLSurface surface() const {
|
||||
return m_surface;
|
||||
}
|
||||
EGLConfig config() const {
|
||||
return m_config;
|
||||
}
|
||||
void setEglDisplay(const EGLDisplay &display) {
|
||||
m_display = display;
|
||||
}
|
||||
void setContext(const EGLContext &context) {
|
||||
m_context = context;
|
||||
}
|
||||
void setSurface(const EGLSurface &surface) {
|
||||
m_surface = surface;
|
||||
}
|
||||
void setConfig(const EGLConfig &config) {
|
||||
m_config = config;
|
||||
}
|
||||
void cleanup();
|
||||
bool initEglAPI();
|
||||
void initKWinGL();
|
||||
void initBufferAge();
|
||||
void initClientExtensions();
|
||||
void initWayland();
|
||||
bool hasClientExtension(const QByteArray &ext) const;
|
||||
|
||||
private:
|
||||
EGLDisplay m_display = EGL_NO_DISPLAY;
|
||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
||||
EGLContext m_context = EGL_NO_CONTEXT;
|
||||
EGLConfig m_config = nullptr;
|
||||
QList<QByteArray> m_clientExtensions;
|
||||
};
|
||||
|
||||
class AbstractEglTexture : public SceneOpenGL::TexturePrivate
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractEglTexture();
|
||||
bool loadTexture(WindowPixmap *pixmap) override;
|
||||
void updateTexture(WindowPixmap *pixmap) override;
|
||||
OpenGLBackend *backend() override;
|
||||
|
||||
protected:
|
||||
AbstractEglTexture(SceneOpenGL::Texture *texture, AbstractEglBackend *backend);
|
||||
EGLImageKHR image() const {
|
||||
return m_image;
|
||||
}
|
||||
|
||||
private:
|
||||
bool loadTexture(xcb_pixmap_t pix, const QSize &size);
|
||||
#if HAVE_WAYLAND
|
||||
bool loadShmTexture(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
bool loadEglTexture(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
EGLImageKHR attach(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
#endif
|
||||
SceneOpenGL::Texture *q;
|
||||
AbstractEglBackend *m_backend;
|
||||
EGLImageKHR m_image;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -37,27 +37,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
typedef GLboolean(*eglBindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display);
|
||||
typedef GLboolean(*eglUnbindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display);
|
||||
typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
|
||||
eglBindWaylandDisplayWL_func eglBindWaylandDisplayWL = nullptr;
|
||||
eglUnbindWaylandDisplayWL_func eglUnbindWaylandDisplayWL = nullptr;
|
||||
eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
|
||||
|
||||
#ifndef EGL_WAYLAND_BUFFER_WL
|
||||
#define EGL_WAYLAND_BUFFER_WL 0x31D5
|
||||
#endif
|
||||
#ifndef EGL_WAYLAND_PLANE_WL
|
||||
#define EGL_WAYLAND_PLANE_WL 0x31D6
|
||||
#endif
|
||||
#ifndef EGL_WAYLAND_Y_INVERTED_WL
|
||||
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
|
||||
#endif
|
||||
|
||||
EglWaylandBackend::EglWaylandBackend()
|
||||
: QObject(NULL)
|
||||
, OpenGLBackend()
|
||||
, m_context(EGL_NO_CONTEXT)
|
||||
, AbstractEglBackend()
|
||||
, m_bufferAge(0)
|
||||
, m_wayland(Wayland::WaylandBackend::self())
|
||||
, m_overlay(NULL)
|
||||
|
@ -84,15 +66,7 @@ EglWaylandBackend::EglWaylandBackend()
|
|||
|
||||
EglWaylandBackend::~EglWaylandBackend()
|
||||
{
|
||||
if (eglUnbindWaylandDisplayWL && m_display != EGL_NO_DISPLAY) {
|
||||
eglUnbindWaylandDisplayWL(m_display, *(WaylandServer::self()->display()));
|
||||
}
|
||||
cleanupGL();
|
||||
doneCurrent();
|
||||
eglDestroyContext(m_display, m_context);
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
eglTerminate(m_display);
|
||||
eglReleaseThread();
|
||||
cleanup();
|
||||
if (m_overlay) {
|
||||
wl_egl_window_destroy(m_overlay);
|
||||
}
|
||||
|
@ -100,53 +74,26 @@ EglWaylandBackend::~EglWaylandBackend()
|
|||
|
||||
bool EglWaylandBackend::initializeEgl()
|
||||
{
|
||||
// Get the list of client extensions
|
||||
const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
|
||||
if (clientExtensionsString.isEmpty()) {
|
||||
// If eglQueryString() returned NULL, the implementation doesn't support
|
||||
// EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
|
||||
(void) eglGetError();
|
||||
}
|
||||
|
||||
const QList<QByteArray> clientExtensions = clientExtensionsString.split(' ');
|
||||
initClientExtensions();
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
|
||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||
// if the implementation supports it.
|
||||
m_havePlatformBase = clientExtensions.contains(QByteArrayLiteral("EGL_EXT_platform_base"));
|
||||
m_havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base"));
|
||||
if (m_havePlatformBase) {
|
||||
// Make sure that the wayland platform is supported
|
||||
if (!clientExtensions.contains(QByteArrayLiteral("EGL_EXT_platform_wayland")))
|
||||
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_wayland")))
|
||||
return false;
|
||||
|
||||
m_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr);
|
||||
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr);
|
||||
} else {
|
||||
m_display = eglGetDisplay(m_wayland->display());
|
||||
display = eglGetDisplay(m_wayland->display());
|
||||
}
|
||||
|
||||
if (m_display == EGL_NO_DISPLAY)
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
return false;
|
||||
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(m_display, &major, &minor) == EGL_FALSE)
|
||||
return false;
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
qCWarning(KWIN_CORE) << "Error during eglInitialize " << error;
|
||||
return false;
|
||||
}
|
||||
qCDebug(KWIN_CORE) << "Egl Initialize succeeded";
|
||||
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
#else
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "bind OpenGL API failed";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
qCDebug(KWIN_CORE) << "EGL version: " << major << "." << minor;
|
||||
return true;
|
||||
setEglDisplay(display);
|
||||
return initEglAPI();
|
||||
}
|
||||
|
||||
void EglWaylandBackend::init()
|
||||
|
@ -156,43 +103,23 @@ void EglWaylandBackend::init()
|
|||
return;
|
||||
}
|
||||
|
||||
initEGL();
|
||||
GLPlatform *glPlatform = GLPlatform::instance();
|
||||
glPlatform->detect(EglPlatformInterface);
|
||||
glPlatform->printResults();
|
||||
initGL(EglPlatformInterface);
|
||||
|
||||
setSupportsBufferAge(false);
|
||||
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_EXT_buffer_age"))) {
|
||||
const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
|
||||
|
||||
if (useBufferAge != "0")
|
||||
setSupportsBufferAge(true);
|
||||
}
|
||||
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_WL_bind_wayland_display"))) {
|
||||
eglBindWaylandDisplayWL = (eglBindWaylandDisplayWL_func)eglGetProcAddress("eglBindWaylandDisplayWL");
|
||||
eglUnbindWaylandDisplayWL = (eglUnbindWaylandDisplayWL_func)eglGetProcAddress("eglUnbindWaylandDisplayWL");
|
||||
eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
|
||||
if (!eglBindWaylandDisplayWL(m_display, *(WaylandServer::self()->display()))) {
|
||||
eglUnbindWaylandDisplayWL = nullptr;
|
||||
eglQueryWaylandBufferWL = nullptr;
|
||||
}
|
||||
}
|
||||
initKWinGL();
|
||||
initBufferAge();
|
||||
initWayland();
|
||||
}
|
||||
|
||||
bool EglWaylandBackend::initRenderingContext()
|
||||
{
|
||||
initBufferConfigs();
|
||||
|
||||
EGLContext context = EGL_NO_CONTEXT;
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
|
||||
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs);
|
||||
#else
|
||||
const EGLint context_attribs_31_core[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
||||
|
@ -205,21 +132,22 @@ bool EglWaylandBackend::initRenderingContext()
|
|||
EGL_NONE
|
||||
};
|
||||
|
||||
const char* eglExtensionsCString = eglQueryString(m_display, EGL_EXTENSIONS);
|
||||
const char* eglExtensionsCString = eglQueryString(eglDisplay(), EGL_EXTENSIONS);
|
||||
const QList<QByteArray> extensions = QByteArray::fromRawData(eglExtensionsCString, qstrlen(eglExtensionsCString)).split(' ');
|
||||
|
||||
// Try to create a 3.1 core context
|
||||
if (options->glCoreProfile() && extensions.contains(QByteArrayLiteral("EGL_KHR_create_context")))
|
||||
m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs_31_core);
|
||||
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_31_core);
|
||||
|
||||
if (m_context == EGL_NO_CONTEXT)
|
||||
m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs_legacy);
|
||||
if (context == EGL_NO_CONTEXT)
|
||||
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_legacy);
|
||||
#endif
|
||||
|
||||
if (m_context == EGL_NO_CONTEXT) {
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
qCCritical(KWIN_CORE) << "Create Context failed";
|
||||
return false;
|
||||
}
|
||||
setContext(context);
|
||||
|
||||
if (!m_wayland->surface()) {
|
||||
return false;
|
||||
|
@ -234,22 +162,24 @@ bool EglWaylandBackend::initRenderingContext()
|
|||
return false;
|
||||
}
|
||||
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
if (m_havePlatformBase)
|
||||
m_surface = eglCreatePlatformWindowSurfaceEXT(m_display, m_config, (void *) m_overlay, nullptr);
|
||||
surface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *) m_overlay, nullptr);
|
||||
else
|
||||
m_surface = eglCreateWindowSurface(m_display, m_config, m_overlay, nullptr);
|
||||
surface = eglCreateWindowSurface(eglDisplay(), config(), m_overlay, nullptr);
|
||||
|
||||
if (m_surface == EGL_NO_SURFACE) {
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
qCCritical(KWIN_CORE) << "Create Window Surface failed";
|
||||
return false;
|
||||
}
|
||||
setSurface(surface);
|
||||
|
||||
return makeContextCurrent();
|
||||
}
|
||||
|
||||
bool EglWaylandBackend::makeContextCurrent()
|
||||
{
|
||||
if (eglMakeCurrent(m_display, m_surface, m_surface, m_context) == EGL_FALSE) {
|
||||
if (eglMakeCurrent(eglDisplay(), surface(), surface(), context()) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "Make Context Current failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -281,7 +211,7 @@ bool EglWaylandBackend::initBufferConfigs()
|
|||
|
||||
EGLint count;
|
||||
EGLConfig configs[1024];
|
||||
if (eglChooseConfig(m_display, config_attribs, configs, 1, &count) == EGL_FALSE) {
|
||||
if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "choose config failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -289,7 +219,7 @@ bool EglWaylandBackend::initBufferConfigs()
|
|||
qCCritical(KWIN_CORE) << "choose config did not return a config" << count;
|
||||
return false;
|
||||
}
|
||||
m_config = configs[0];
|
||||
setConfig(configs[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -300,12 +230,12 @@ void EglWaylandBackend::present()
|
|||
Compositor::self()->aboutToSwapBuffers();
|
||||
|
||||
if (supportsBufferAge()) {
|
||||
eglSwapBuffers(m_display, m_surface);
|
||||
eglQuerySurface(m_display, m_surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||
eglSwapBuffers(eglDisplay(), surface());
|
||||
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||
setLastDamage(QRegion());
|
||||
return;
|
||||
} else {
|
||||
eglSwapBuffers(m_display, m_surface);
|
||||
eglSwapBuffers(eglDisplay(), surface());
|
||||
setLastDamage(QRegion());
|
||||
}
|
||||
}
|
||||
|
@ -373,21 +303,6 @@ void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const Q
|
|||
addToDamageHistory(damagedRegion);
|
||||
}
|
||||
|
||||
bool EglWaylandBackend::makeCurrent()
|
||||
{
|
||||
if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
|
||||
// Workaround to tell Qt that no QOpenGLContext is current
|
||||
context->doneCurrent();
|
||||
}
|
||||
const bool current = eglMakeCurrent(m_display, m_surface, m_surface, m_context);
|
||||
return current;
|
||||
}
|
||||
|
||||
void EglWaylandBackend::doneCurrent()
|
||||
{
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
void EglWaylandBackend::overlaySizeChanged(const QSize &size)
|
||||
{
|
||||
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
|
||||
|
@ -403,166 +318,10 @@ bool EglWaylandBackend::usesOverlayWindow() const
|
|||
************************************************/
|
||||
|
||||
EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglWaylandBackend *backend)
|
||||
: SceneOpenGL::TexturePrivate()
|
||||
, q(texture)
|
||||
, m_backend(backend)
|
||||
: AbstractEglTexture(texture, backend)
|
||||
{
|
||||
m_target = GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
EglWaylandTexture::~EglWaylandTexture()
|
||||
{
|
||||
if (m_image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(m_backend->m_display, m_image);
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLBackend *EglWaylandTexture::backend()
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
bool EglWaylandTexture::loadTexture(WindowPixmap *pixmap)
|
||||
{
|
||||
const auto &buffer = pixmap->buffer();
|
||||
if (buffer.isNull()) {
|
||||
return false;
|
||||
}
|
||||
if (buffer->shmBuffer()) {
|
||||
return loadShmTexture(buffer);
|
||||
} else {
|
||||
return loadEglTexture(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool EglWaylandTexture::loadShmTexture(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
if (GLPlatform::instance()->isGLES()) {
|
||||
// FIXME
|
||||
return false;
|
||||
}
|
||||
const QImage &image = buffer->data();
|
||||
if (image.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
|
||||
const QSize &size = image.size();
|
||||
// TODO: this should be shared with GLTexture(const QImage&, GLenum)
|
||||
GLenum format = 0;
|
||||
switch (image.format()) {
|
||||
case QImage::Format_ARGB32:
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
format = GL_RGBA8;
|
||||
break;
|
||||
case QImage::Format_RGB32:
|
||||
format = GL_RGB8;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
glTexImage2D(m_target, 0, format, size.width(), size.height(), 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
|
||||
|
||||
q->unbind();
|
||||
q->setYInverted(true);
|
||||
m_size = size;
|
||||
updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EglWaylandTexture::loadEglTexture(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
if (!eglQueryWaylandBufferWL) {
|
||||
return false;
|
||||
}
|
||||
if (!buffer->resource()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
m_image = attach(buffer);
|
||||
q->unbind();
|
||||
|
||||
if (EGL_NO_IMAGE_KHR == m_image) {
|
||||
qCDebug(KWIN_CORE) << "failed to create egl image";
|
||||
q->discard();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EglWaylandTexture::updateTexture(WindowPixmap *pixmap)
|
||||
{
|
||||
const auto &buffer = pixmap->buffer();
|
||||
if (buffer.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!buffer->shmBuffer()) {
|
||||
q->bind();
|
||||
EGLImageKHR image = attach(buffer);
|
||||
q->unbind();
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(m_backend->m_display, m_image);
|
||||
m_image = image;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// shm fallback
|
||||
if (GLPlatform::instance()->isGLES()) {
|
||||
// FIXME
|
||||
return;
|
||||
}
|
||||
const QImage &image = buffer->data();
|
||||
if (image.isNull()) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(image.size() == m_size);
|
||||
q->bind();
|
||||
const QRegion &damage = pixmap->toplevel()->damage();
|
||||
|
||||
// TODO: this should be shared with GLTexture::update
|
||||
const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
for (const QRect &rect : damage.rects()) {
|
||||
glTexSubImage2D(m_target, 0, rect.x(), rect.y(), rect.width(), rect.height(),
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, im.copy(rect).bits());
|
||||
}
|
||||
q->unbind();
|
||||
}
|
||||
|
||||
EGLImageKHR EglWaylandTexture::attach(const QPointer< KWayland::Server::BufferInterface > &buffer)
|
||||
{
|
||||
EGLint format, width, height, yInverted;
|
||||
eglQueryWaylandBufferWL(m_backend->m_display, buffer->resource(), EGL_TEXTURE_FORMAT, &format);
|
||||
if (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA) {
|
||||
qCDebug(KWIN_CORE) << "Unsupported texture format: " << format;
|
||||
return EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
eglQueryWaylandBufferWL(m_backend->m_display, buffer->resource(), EGL_WAYLAND_Y_INVERTED_WL, &yInverted);
|
||||
eglQueryWaylandBufferWL(m_backend->m_display, buffer->resource(), EGL_WIDTH, &width);
|
||||
eglQueryWaylandBufferWL(m_backend->m_display, buffer->resource(), EGL_HEIGHT, &height);
|
||||
|
||||
const EGLint attribs[] = {
|
||||
EGL_WAYLAND_PLANE_WL, 0,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLImageKHR image = eglCreateImageKHR(m_backend->m_display, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL,
|
||||
(EGLClientBuffer)buffer->resource(), attribs);
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
|
||||
m_size = QSize(width, height);
|
||||
updateMatrix();
|
||||
q->setYInverted(yInverted);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
EglWaylandTexture::~EglWaylandTexture() = default;
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#ifndef KWIN_EGL_WAYLAND_BACKEND_H
|
||||
#define KWIN_EGL_WAYLAND_BACKEND_H
|
||||
#include "abstract_egl_backend.h"
|
||||
#include "scene_opengl.h"
|
||||
// wayland
|
||||
#include <wayland-egl.h>
|
||||
|
@ -46,7 +47,7 @@ namespace Wayland {
|
|||
* repaints, which is obviously not optimal. Best solution is probably to go for buffer_age extension
|
||||
* and make it the only available solution next to fullscreen repaints.
|
||||
**/
|
||||
class EglWaylandBackend : public QObject, public OpenGLBackend
|
||||
class EglWaylandBackend : public QObject, public AbstractEglBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -56,8 +57,6 @@ public:
|
|||
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
||||
virtual QRegion prepareRenderingFrame();
|
||||
virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion);
|
||||
virtual bool makeCurrent() override;
|
||||
virtual void doneCurrent() override;
|
||||
virtual bool usesOverlayWindow() const override;
|
||||
|
||||
protected:
|
||||
|
@ -72,10 +71,6 @@ private:
|
|||
bool initBufferConfigs();
|
||||
bool initRenderingContext();
|
||||
bool makeContextCurrent();
|
||||
EGLDisplay m_display;
|
||||
EGLConfig m_config;
|
||||
EGLSurface m_surface;
|
||||
EGLContext m_context;
|
||||
int m_bufferAge;
|
||||
Wayland::WaylandBackend *m_wayland;
|
||||
wl_egl_window *m_overlay;
|
||||
|
@ -86,23 +81,14 @@ private:
|
|||
/**
|
||||
* @brief Texture using an EGLImageKHR.
|
||||
**/
|
||||
class EglWaylandTexture : public SceneOpenGL::TexturePrivate
|
||||
class EglWaylandTexture : public AbstractEglTexture
|
||||
{
|
||||
public:
|
||||
virtual ~EglWaylandTexture();
|
||||
virtual bool loadTexture(WindowPixmap *pixmap) override;
|
||||
virtual void updateTexture(WindowPixmap *pixmap) override;
|
||||
virtual OpenGLBackend *backend();
|
||||
|
||||
private:
|
||||
friend class EglWaylandBackend;
|
||||
EglWaylandTexture(SceneOpenGL::Texture *texture, EglWaylandBackend *backend);
|
||||
bool loadShmTexture(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
bool loadEglTexture(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
EGLImageKHR attach(const QPointer<KWayland::Server::BufferInterface> &buffer);
|
||||
SceneOpenGL::Texture *q;
|
||||
EglWaylandBackend *m_backend;
|
||||
EGLImageKHR m_image;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -39,9 +39,8 @@ namespace KWin
|
|||
{
|
||||
|
||||
EglOnXBackend::EglOnXBackend()
|
||||
: OpenGLBackend()
|
||||
: AbstractEglBackend()
|
||||
, m_overlayWindow(new OverlayWindow())
|
||||
, ctx(EGL_NO_CONTEXT)
|
||||
, surfaceHasSubPost(0)
|
||||
, m_bufferAge(0)
|
||||
, m_usesOverlayWindow(true)
|
||||
|
@ -57,9 +56,8 @@ EglOnXBackend::EglOnXBackend()
|
|||
|
||||
#if HAVE_X11_XCB
|
||||
EglOnXBackend::EglOnXBackend(X11WindowedBackend *backend)
|
||||
: OpenGLBackend()
|
||||
: AbstractEglBackend()
|
||||
, m_overlayWindow(nullptr)
|
||||
, ctx(EGL_NO_CONTEXT)
|
||||
, surfaceHasSubPost(0)
|
||||
, m_bufferAge(0)
|
||||
, m_usesOverlayWindow(false)
|
||||
|
@ -81,12 +79,7 @@ EglOnXBackend::~EglOnXBackend()
|
|||
if (isFailed() && m_overlayWindow) {
|
||||
m_overlayWindow->destroy();
|
||||
}
|
||||
cleanupGL();
|
||||
doneCurrent();
|
||||
eglDestroyContext(dpy, ctx);
|
||||
eglDestroySurface(dpy, surface);
|
||||
eglTerminate(dpy);
|
||||
eglReleaseThread();
|
||||
cleanup();
|
||||
if (m_overlayWindow) {
|
||||
if (overlayWindow()->window()) {
|
||||
overlayWindow()->destroy();
|
||||
|
@ -105,22 +98,13 @@ void EglOnXBackend::init()
|
|||
return;
|
||||
}
|
||||
|
||||
initEGL();
|
||||
initKWinGL();
|
||||
if (!hasGLExtension(QByteArrayLiteral("EGL_KHR_image")) &&
|
||||
(!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_base")) ||
|
||||
!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_pixmap")))) {
|
||||
setFailed(QStringLiteral("Required support for binding pixmaps to EGLImages not found, disabling compositing"));
|
||||
return;
|
||||
}
|
||||
GLPlatform *glPlatform = GLPlatform::instance();
|
||||
glPlatform->detect(EglPlatformInterface);
|
||||
if (GLPlatform::instance()->driver() == Driver_Intel)
|
||||
options->setUnredirectFullscreen(false); // bug #252817
|
||||
options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting
|
||||
if (options->glPreferBufferSwap() == Options::AutoSwapStrategy)
|
||||
options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen
|
||||
glPlatform->printResults();
|
||||
initGL(EglPlatformInterface);
|
||||
if (!hasGLExtension(QByteArrayLiteral("GL_OES_EGL_image"))) {
|
||||
setFailed(QStringLiteral("Required extension GL_OES_EGL_image not found, disabling compositing"));
|
||||
return;
|
||||
|
@ -128,7 +112,7 @@ void EglOnXBackend::init()
|
|||
|
||||
// check for EGL_NV_post_sub_buffer and whether it can be used on the surface
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_NV_post_sub_buffer"))) {
|
||||
if (eglQuerySurface(dpy, surface, EGL_POST_SUB_BUFFER_SUPPORTED_NV, &surfaceHasSubPost) == EGL_FALSE) {
|
||||
if (eglQuerySurface(eglDisplay(), surface(), EGL_POST_SUB_BUFFER_SUPPORTED_NV, &surfaceHasSubPost) == EGL_FALSE) {
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS && error != EGL_BAD_ATTRIBUTE) {
|
||||
setFailed(QStringLiteral("query surface failed"));
|
||||
|
@ -139,14 +123,7 @@ void EglOnXBackend::init()
|
|||
}
|
||||
}
|
||||
|
||||
setSupportsBufferAge(false);
|
||||
|
||||
if (hasGLExtension(QByteArrayLiteral("EGL_EXT_buffer_age"))) {
|
||||
const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
|
||||
|
||||
if (useBufferAge != "0")
|
||||
setSupportsBufferAge(true);
|
||||
}
|
||||
initBufferAge();
|
||||
|
||||
setSyncsToVBlank(false);
|
||||
setBlocksForRetrace(false);
|
||||
|
@ -158,9 +135,9 @@ void EglOnXBackend::init()
|
|||
if (options->glPreferBufferSwap() != Options::NoSwapEncourage) {
|
||||
// check if swap interval 1 is supported
|
||||
EGLint val;
|
||||
eglGetConfigAttrib(dpy, config, EGL_MAX_SWAP_INTERVAL, &val);
|
||||
eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val);
|
||||
if (val >= 1) {
|
||||
if (eglSwapInterval(dpy, 1)) {
|
||||
if (eglSwapInterval(eglDisplay(), 1)) {
|
||||
qCDebug(KWIN_CORE) << "Enabled v-sync";
|
||||
setSyncsToVBlank(true);
|
||||
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
|
||||
|
@ -175,7 +152,7 @@ void EglOnXBackend::init()
|
|||
}
|
||||
} else {
|
||||
// disable v-sync
|
||||
eglSwapInterval(dpy, 0);
|
||||
eglSwapInterval(eglDisplay(), 0);
|
||||
}
|
||||
} else {
|
||||
/* In the GLX backend, we fall back to using glCopyPixels if we have no extension providing support for partial screen updates.
|
||||
|
@ -184,28 +161,23 @@ void EglOnXBackend::init()
|
|||
* eglSwapBuffers() for each frame. eglSwapBuffers() then does the copy (no page flip possible in this mode),
|
||||
* which means it is slow and not synced to the v-blank. */
|
||||
qCWarning(KWIN_CORE) << "eglPostSubBufferNV not supported, have to enable buffer preservation - which breaks v-sync and performance";
|
||||
eglSurfaceAttrib(dpy, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
|
||||
eglSurfaceAttrib(eglDisplay(), surface(), EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
|
||||
}
|
||||
|
||||
initWayland();
|
||||
}
|
||||
|
||||
bool EglOnXBackend::initRenderingContext()
|
||||
{
|
||||
// Get the list of client extensions
|
||||
const QByteArray clientExtensionString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
if (clientExtensionString.isEmpty()) {
|
||||
// If eglQueryString() returned NULL, the implementation doesn't support
|
||||
// EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
|
||||
(void) eglGetError();
|
||||
}
|
||||
|
||||
const QList<QByteArray> clientExtensions = clientExtensionString.split(' ');
|
||||
initClientExtensions();
|
||||
EGLDisplay dpy;
|
||||
|
||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||
// if the implementation supports it.
|
||||
const bool havePlatformBase = clientExtensions.contains("EGL_EXT_platform_base");
|
||||
const bool havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base"));
|
||||
if (havePlatformBase) {
|
||||
// Make sure that the X11 platform is supported
|
||||
if (!clientExtensions.contains("EGL_EXT_platform_x11"))
|
||||
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_x11")))
|
||||
return false;
|
||||
|
||||
const int attribs[] = {
|
||||
|
@ -220,19 +192,8 @@ bool EglOnXBackend::initRenderingContext()
|
|||
|
||||
if (dpy == EGL_NO_DISPLAY)
|
||||
return false;
|
||||
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(dpy, &major, &minor) == EGL_FALSE)
|
||||
return false;
|
||||
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
#else
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "bind OpenGL API failed";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
setEglDisplay(dpy);
|
||||
initEglAPI();
|
||||
|
||||
initBufferConfigs();
|
||||
|
||||
|
@ -258,23 +219,30 @@ bool EglOnXBackend::initRenderingContext()
|
|||
return false;
|
||||
}
|
||||
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
if (havePlatformBase) {
|
||||
// Note: Window is 64 bits on a 64-bit architecture whereas xcb_window_t is
|
||||
// always 32 bits. eglCreatePlatformWindowSurfaceEXT() expects the
|
||||
// native_window parameter to be pointer to a Window, so this variable
|
||||
// cannot be an xcb_window_t.
|
||||
surface = eglCreatePlatformWindowSurfaceEXT(dpy, config, (void *) &window, nullptr);
|
||||
surface = eglCreatePlatformWindowSurfaceEXT(dpy, config(), (void *) &window, nullptr);
|
||||
} else {
|
||||
surface = eglCreateWindowSurface(dpy, config, window, nullptr);
|
||||
surface = eglCreateWindowSurface(dpy, config(), window, nullptr);
|
||||
}
|
||||
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
return false;
|
||||
}
|
||||
setSurface(surface);
|
||||
|
||||
EGLContext ctx = EGL_NO_CONTEXT;
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs);
|
||||
ctx = eglCreateContext(dpy, config(), EGL_NO_CONTEXT, context_attribs);
|
||||
#else
|
||||
const EGLint context_attribs_31_core[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
||||
|
@ -291,24 +259,23 @@ bool EglOnXBackend::initRenderingContext()
|
|||
|
||||
// Try to create a 3.1 core context
|
||||
if (options->glCoreProfile() && extensions.contains("EGL_KHR_create_context"))
|
||||
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs_31_core);
|
||||
ctx = eglCreateContext(dpy, config(), EGL_NO_CONTEXT, context_attribs_31_core);
|
||||
|
||||
if (ctx == EGL_NO_CONTEXT)
|
||||
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs_legacy);
|
||||
ctx = eglCreateContext(dpy, config(), EGL_NO_CONTEXT, context_attribs_legacy);
|
||||
#endif
|
||||
|
||||
if (ctx == EGL_NO_CONTEXT) {
|
||||
qCCritical(KWIN_CORE) << "Create Context failed";
|
||||
return false;
|
||||
}
|
||||
setContext(ctx);
|
||||
|
||||
if (eglMakeCurrent(dpy, surface, surface, ctx) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "Make Context Current failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
qCDebug(KWIN_CORE) << "EGL version: " << major << "." << minor;
|
||||
|
||||
EGLint error = eglGetError();
|
||||
if (error != EGL_SUCCESS) {
|
||||
qCWarning(KWIN_CORE) << "Error occurred while creating context " << error;
|
||||
|
@ -337,7 +304,7 @@ bool EglOnXBackend::initBufferConfigs()
|
|||
|
||||
EGLint count;
|
||||
EGLConfig configs[1024];
|
||||
if (eglChooseConfig(dpy, config_attribs, configs, 1024, &count) == EGL_FALSE) {
|
||||
if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1024, &count) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "choose config failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -350,14 +317,14 @@ bool EglOnXBackend::initBufferConfigs()
|
|||
return false;
|
||||
}
|
||||
|
||||
config = configs[0];
|
||||
setConfig(configs[0]);
|
||||
for (int i = 0; i < count; i++) {
|
||||
EGLint val;
|
||||
if (eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &val) == EGL_FALSE) {
|
||||
if (eglGetConfigAttrib(eglDisplay(), configs[i], EGL_NATIVE_VISUAL_ID, &val) == EGL_FALSE) {
|
||||
qCCritical(KWIN_CORE) << "egl get config attrib failed";
|
||||
}
|
||||
if (uint32_t(val) == attribs->visual) {
|
||||
config = configs[i];
|
||||
setConfig(configs[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +346,7 @@ void EglOnXBackend::present()
|
|||
m_swapProfiler.begin();
|
||||
}
|
||||
// the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation)
|
||||
eglSwapBuffers(dpy, surface);
|
||||
eglSwapBuffers(eglDisplay(), surface());
|
||||
if (gs_tripleBufferNeedsDetection) {
|
||||
eglWaitGL();
|
||||
if (char result = m_swapProfiler.end()) {
|
||||
|
@ -388,7 +355,7 @@ void EglOnXBackend::present()
|
|||
// TODO this is a workaround, we should get __GL_YIELD set before libGL checks it
|
||||
if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) {
|
||||
options->setGlPreferBufferSwap(0);
|
||||
eglSwapInterval(dpy, 0);
|
||||
eglSwapInterval(eglDisplay(), 0);
|
||||
qCWarning(KWIN_CORE) << "\nIt seems you are using the nvidia driver without triple buffering\n"
|
||||
"You must export __GL_YIELD=\"USLEEP\" to prevent large CPU overhead on synced swaps\n"
|
||||
"Preferably, enable the TripleBuffer Option in the xorg.conf Device\n"
|
||||
|
@ -400,12 +367,12 @@ void EglOnXBackend::present()
|
|||
}
|
||||
}
|
||||
if (supportsBufferAge()) {
|
||||
eglQuerySurface(dpy, surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||
}
|
||||
} else {
|
||||
// a part of the screen changed, and we can use eglPostSubBufferNV to copy the updated area
|
||||
foreach (const QRect & r, lastDamage().rects()) {
|
||||
eglPostSubBufferNV(dpy, surface, r.left(), screenSize.height() - r.bottom() - 1, r.width(), r.height());
|
||||
eglPostSubBufferNV(eglDisplay(), surface(), r.left(), screenSize.height() - r.bottom() - 1, r.width(), r.height());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,21 +461,6 @@ void EglOnXBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegi
|
|||
addToDamageHistory(damagedRegion);
|
||||
}
|
||||
|
||||
bool EglOnXBackend::makeCurrent()
|
||||
{
|
||||
if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
|
||||
// Workaround to tell Qt that no QOpenGLContext is current
|
||||
context->doneCurrent();
|
||||
}
|
||||
const bool current = eglMakeCurrent(dpy, surface, surface, ctx);
|
||||
return current;
|
||||
}
|
||||
|
||||
void EglOnXBackend::doneCurrent()
|
||||
{
|
||||
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
bool EglOnXBackend::usesOverlayWindow() const
|
||||
{
|
||||
return m_usesOverlayWindow;
|
||||
|
@ -524,60 +476,11 @@ OverlayWindow* EglOnXBackend::overlayWindow()
|
|||
************************************************/
|
||||
|
||||
EglTexture::EglTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglOnXBackend *backend)
|
||||
: SceneOpenGL::TexturePrivate()
|
||||
, q(texture)
|
||||
, m_backend(backend)
|
||||
, m_image(EGL_NO_IMAGE_KHR)
|
||||
: AbstractEglTexture(texture, backend)
|
||||
{
|
||||
m_target = GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
EglTexture::~EglTexture()
|
||||
{
|
||||
if (m_image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(m_backend->dpy, m_image);
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLBackend *EglTexture::backend()
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
bool EglTexture::loadTexture(xcb_pixmap_t pix, const QSize &size)
|
||||
{
|
||||
if (pix == XCB_NONE)
|
||||
return false;
|
||||
|
||||
glGenTextures(1, &m_texture);
|
||||
q->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
q->setFilter(GL_LINEAR);
|
||||
q->bind();
|
||||
const EGLint attribs[] = {
|
||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||
EGL_NONE
|
||||
};
|
||||
m_image = eglCreateImageKHR(m_backend->dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
||||
(EGLClientBuffer)pix, attribs);
|
||||
|
||||
if (EGL_NO_IMAGE_KHR == m_image) {
|
||||
qCDebug(KWIN_CORE) << "failed to create egl image";
|
||||
q->unbind();
|
||||
q->discard();
|
||||
return false;
|
||||
}
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)m_image);
|
||||
q->unbind();
|
||||
q->setYInverted(true);
|
||||
m_size = size;
|
||||
updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EglTexture::loadTexture(WindowPixmap *pixmap)
|
||||
{
|
||||
return loadTexture(pixmap->pixmap(), pixmap->toplevel()->size());
|
||||
}
|
||||
EglTexture::~EglTexture() = default;
|
||||
|
||||
void KWin::EglTexture::onDamage()
|
||||
{
|
||||
|
@ -585,7 +488,7 @@ void KWin::EglTexture::onDamage()
|
|||
// This is just implemented to be consistent with
|
||||
// the example in mesa/demos/src/egl/opengles1/texture_from_pixmap.c
|
||||
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) m_image);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) image());
|
||||
}
|
||||
GLTexturePrivate::onDamage();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#ifndef KWIN_EGL_ON_X_BACKEND_H
|
||||
#define KWIN_EGL_ON_X_BACKEND_H
|
||||
#include "abstract_egl_backend.h"
|
||||
#include "scene_opengl.h"
|
||||
|
||||
namespace KWin
|
||||
|
@ -29,7 +30,7 @@ class X11WindowedBackend;
|
|||
/**
|
||||
* @brief OpenGL Backend using Egl windowing system over an X overlay window.
|
||||
**/
|
||||
class EglOnXBackend : public OpenGLBackend
|
||||
class EglOnXBackend : public AbstractEglBackend
|
||||
{
|
||||
public:
|
||||
EglOnXBackend();
|
||||
|
@ -41,8 +42,6 @@ public:
|
|||
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
||||
virtual QRegion prepareRenderingFrame();
|
||||
virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion);
|
||||
virtual bool makeCurrent() override;
|
||||
virtual void doneCurrent() override;
|
||||
virtual OverlayWindow* overlayWindow() override;
|
||||
virtual bool usesOverlayWindow() const override;
|
||||
|
||||
|
@ -57,10 +56,6 @@ private:
|
|||
* @brief The OverlayWindow used by this Backend.
|
||||
**/
|
||||
OverlayWindow *m_overlayWindow;
|
||||
EGLDisplay dpy;
|
||||
EGLConfig config;
|
||||
EGLSurface surface;
|
||||
EGLContext ctx;
|
||||
int surfaceHasSubPost;
|
||||
int m_bufferAge;
|
||||
bool m_usesOverlayWindow;
|
||||
|
@ -77,21 +72,15 @@ private:
|
|||
/**
|
||||
* @brief Texture using an EGLImageKHR.
|
||||
**/
|
||||
class EglTexture : public SceneOpenGL::TexturePrivate
|
||||
class EglTexture : public AbstractEglTexture
|
||||
{
|
||||
public:
|
||||
virtual ~EglTexture();
|
||||
virtual void onDamage();
|
||||
virtual bool loadTexture(WindowPixmap *pixmap) override;
|
||||
virtual OpenGLBackend *backend();
|
||||
|
||||
private:
|
||||
friend class EglOnXBackend;
|
||||
EglTexture(SceneOpenGL::Texture *texture, EglOnXBackend *backend);
|
||||
bool loadTexture(xcb_pixmap_t pix, const QSize &size);
|
||||
SceneOpenGL::Texture *q;
|
||||
EglOnXBackend *m_backend;
|
||||
EGLImageKHR m_image;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in New Issue