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()
|
endif()
|
||||||
|
|
||||||
if(KWIN_HAVE_EGL)
|
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()
|
endif()
|
||||||
|
|
||||||
if(HAVE_WAYLAND)
|
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
|
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()
|
EglWaylandBackend::EglWaylandBackend()
|
||||||
: QObject(NULL)
|
: QObject(NULL)
|
||||||
, OpenGLBackend()
|
, AbstractEglBackend()
|
||||||
, m_context(EGL_NO_CONTEXT)
|
|
||||||
, m_bufferAge(0)
|
, m_bufferAge(0)
|
||||||
, m_wayland(Wayland::WaylandBackend::self())
|
, m_wayland(Wayland::WaylandBackend::self())
|
||||||
, m_overlay(NULL)
|
, m_overlay(NULL)
|
||||||
|
@ -84,15 +66,7 @@ EglWaylandBackend::EglWaylandBackend()
|
||||||
|
|
||||||
EglWaylandBackend::~EglWaylandBackend()
|
EglWaylandBackend::~EglWaylandBackend()
|
||||||
{
|
{
|
||||||
if (eglUnbindWaylandDisplayWL && m_display != EGL_NO_DISPLAY) {
|
cleanup();
|
||||||
eglUnbindWaylandDisplayWL(m_display, *(WaylandServer::self()->display()));
|
|
||||||
}
|
|
||||||
cleanupGL();
|
|
||||||
doneCurrent();
|
|
||||||
eglDestroyContext(m_display, m_context);
|
|
||||||
eglDestroySurface(m_display, m_surface);
|
|
||||||
eglTerminate(m_display);
|
|
||||||
eglReleaseThread();
|
|
||||||
if (m_overlay) {
|
if (m_overlay) {
|
||||||
wl_egl_window_destroy(m_overlay);
|
wl_egl_window_destroy(m_overlay);
|
||||||
}
|
}
|
||||||
|
@ -100,53 +74,26 @@ EglWaylandBackend::~EglWaylandBackend()
|
||||||
|
|
||||||
bool EglWaylandBackend::initializeEgl()
|
bool EglWaylandBackend::initializeEgl()
|
||||||
{
|
{
|
||||||
// Get the list of client extensions
|
initClientExtensions();
|
||||||
const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
EGLDisplay display = EGL_NO_DISPLAY;
|
||||||
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(' ');
|
|
||||||
|
|
||||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||||
// if the implementation supports it.
|
// 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) {
|
if (m_havePlatformBase) {
|
||||||
// Make sure that the wayland platform is supported
|
// 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;
|
return false;
|
||||||
|
|
||||||
m_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr);
|
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr);
|
||||||
} else {
|
} 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;
|
return false;
|
||||||
|
setEglDisplay(display);
|
||||||
EGLint major, minor;
|
return initEglAPI();
|
||||||
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 EglWaylandBackend::init()
|
void EglWaylandBackend::init()
|
||||||
|
@ -156,43 +103,23 @@ void EglWaylandBackend::init()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
initEGL();
|
initKWinGL();
|
||||||
GLPlatform *glPlatform = GLPlatform::instance();
|
initBufferAge();
|
||||||
glPlatform->detect(EglPlatformInterface);
|
initWayland();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EglWaylandBackend::initRenderingContext()
|
bool EglWaylandBackend::initRenderingContext()
|
||||||
{
|
{
|
||||||
initBufferConfigs();
|
initBufferConfigs();
|
||||||
|
|
||||||
|
EGLContext context = EGL_NO_CONTEXT;
|
||||||
#ifdef KWIN_HAVE_OPENGLES
|
#ifdef KWIN_HAVE_OPENGLES
|
||||||
const EGLint context_attribs[] = {
|
const EGLint context_attribs[] = {
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
|
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs);
|
||||||
#else
|
#else
|
||||||
const EGLint context_attribs_31_core[] = {
|
const EGLint context_attribs_31_core[] = {
|
||||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
||||||
|
@ -205,21 +132,22 @@ bool EglWaylandBackend::initRenderingContext()
|
||||||
EGL_NONE
|
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(' ');
|
const QList<QByteArray> extensions = QByteArray::fromRawData(eglExtensionsCString, qstrlen(eglExtensionsCString)).split(' ');
|
||||||
|
|
||||||
// Try to create a 3.1 core context
|
// Try to create a 3.1 core context
|
||||||
if (options->glCoreProfile() && extensions.contains(QByteArrayLiteral("EGL_KHR_create_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)
|
if (context == EGL_NO_CONTEXT)
|
||||||
m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs_legacy);
|
context = eglCreateContext(eglDisplay(), config(), EGL_NO_CONTEXT, context_attribs_legacy);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_context == EGL_NO_CONTEXT) {
|
if (context == EGL_NO_CONTEXT) {
|
||||||
qCCritical(KWIN_CORE) << "Create Context failed";
|
qCCritical(KWIN_CORE) << "Create Context failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setContext(context);
|
||||||
|
|
||||||
if (!m_wayland->surface()) {
|
if (!m_wayland->surface()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -234,22 +162,24 @@ bool EglWaylandBackend::initRenderingContext()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EGLSurface surface = EGL_NO_SURFACE;
|
||||||
if (m_havePlatformBase)
|
if (m_havePlatformBase)
|
||||||
m_surface = eglCreatePlatformWindowSurfaceEXT(m_display, m_config, (void *) m_overlay, nullptr);
|
surface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *) m_overlay, nullptr);
|
||||||
else
|
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";
|
qCCritical(KWIN_CORE) << "Create Window Surface failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setSurface(surface);
|
||||||
|
|
||||||
return makeContextCurrent();
|
return makeContextCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EglWaylandBackend::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";
|
qCCritical(KWIN_CORE) << "Make Context Current failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -281,7 +211,7 @@ bool EglWaylandBackend::initBufferConfigs()
|
||||||
|
|
||||||
EGLint count;
|
EGLint count;
|
||||||
EGLConfig configs[1024];
|
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";
|
qCCritical(KWIN_CORE) << "choose config failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +219,7 @@ bool EglWaylandBackend::initBufferConfigs()
|
||||||
qCCritical(KWIN_CORE) << "choose config did not return a config" << count;
|
qCCritical(KWIN_CORE) << "choose config did not return a config" << count;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_config = configs[0];
|
setConfig(configs[0]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -300,12 +230,12 @@ void EglWaylandBackend::present()
|
||||||
Compositor::self()->aboutToSwapBuffers();
|
Compositor::self()->aboutToSwapBuffers();
|
||||||
|
|
||||||
if (supportsBufferAge()) {
|
if (supportsBufferAge()) {
|
||||||
eglSwapBuffers(m_display, m_surface);
|
eglSwapBuffers(eglDisplay(), surface());
|
||||||
eglQuerySurface(m_display, m_surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||||
setLastDamage(QRegion());
|
setLastDamage(QRegion());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
eglSwapBuffers(m_display, m_surface);
|
eglSwapBuffers(eglDisplay(), surface());
|
||||||
setLastDamage(QRegion());
|
setLastDamage(QRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,21 +303,6 @@ void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const Q
|
||||||
addToDamageHistory(damagedRegion);
|
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)
|
void EglWaylandBackend::overlaySizeChanged(const QSize &size)
|
||||||
{
|
{
|
||||||
wl_egl_window_resize(m_overlay, size.width(), size.height(), 0, 0);
|
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)
|
EglWaylandTexture::EglWaylandTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglWaylandBackend *backend)
|
||||||
: SceneOpenGL::TexturePrivate()
|
: AbstractEglTexture(texture, backend)
|
||||||
, q(texture)
|
|
||||||
, m_backend(backend)
|
|
||||||
{
|
{
|
||||||
m_target = GL_TEXTURE_2D;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EglWaylandTexture::~EglWaylandTexture()
|
EglWaylandTexture::~EglWaylandTexture() = default;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
#ifndef KWIN_EGL_WAYLAND_BACKEND_H
|
#ifndef KWIN_EGL_WAYLAND_BACKEND_H
|
||||||
#define KWIN_EGL_WAYLAND_BACKEND_H
|
#define KWIN_EGL_WAYLAND_BACKEND_H
|
||||||
|
#include "abstract_egl_backend.h"
|
||||||
#include "scene_opengl.h"
|
#include "scene_opengl.h"
|
||||||
// wayland
|
// wayland
|
||||||
#include <wayland-egl.h>
|
#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
|
* 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.
|
* 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
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -56,8 +57,6 @@ public:
|
||||||
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
||||||
virtual QRegion prepareRenderingFrame();
|
virtual QRegion prepareRenderingFrame();
|
||||||
virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion);
|
virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion);
|
||||||
virtual bool makeCurrent() override;
|
|
||||||
virtual void doneCurrent() override;
|
|
||||||
virtual bool usesOverlayWindow() const override;
|
virtual bool usesOverlayWindow() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -72,10 +71,6 @@ private:
|
||||||
bool initBufferConfigs();
|
bool initBufferConfigs();
|
||||||
bool initRenderingContext();
|
bool initRenderingContext();
|
||||||
bool makeContextCurrent();
|
bool makeContextCurrent();
|
||||||
EGLDisplay m_display;
|
|
||||||
EGLConfig m_config;
|
|
||||||
EGLSurface m_surface;
|
|
||||||
EGLContext m_context;
|
|
||||||
int m_bufferAge;
|
int m_bufferAge;
|
||||||
Wayland::WaylandBackend *m_wayland;
|
Wayland::WaylandBackend *m_wayland;
|
||||||
wl_egl_window *m_overlay;
|
wl_egl_window *m_overlay;
|
||||||
|
@ -86,23 +81,14 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief Texture using an EGLImageKHR.
|
* @brief Texture using an EGLImageKHR.
|
||||||
**/
|
**/
|
||||||
class EglWaylandTexture : public SceneOpenGL::TexturePrivate
|
class EglWaylandTexture : public AbstractEglTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~EglWaylandTexture();
|
virtual ~EglWaylandTexture();
|
||||||
virtual bool loadTexture(WindowPixmap *pixmap) override;
|
|
||||||
virtual void updateTexture(WindowPixmap *pixmap) override;
|
|
||||||
virtual OpenGLBackend *backend();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class EglWaylandBackend;
|
friend class EglWaylandBackend;
|
||||||
EglWaylandTexture(SceneOpenGL::Texture *texture, EglWaylandBackend *backend);
|
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
|
} // namespace
|
||||||
|
|
|
@ -39,9 +39,8 @@ namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
EglOnXBackend::EglOnXBackend()
|
EglOnXBackend::EglOnXBackend()
|
||||||
: OpenGLBackend()
|
: AbstractEglBackend()
|
||||||
, m_overlayWindow(new OverlayWindow())
|
, m_overlayWindow(new OverlayWindow())
|
||||||
, ctx(EGL_NO_CONTEXT)
|
|
||||||
, surfaceHasSubPost(0)
|
, surfaceHasSubPost(0)
|
||||||
, m_bufferAge(0)
|
, m_bufferAge(0)
|
||||||
, m_usesOverlayWindow(true)
|
, m_usesOverlayWindow(true)
|
||||||
|
@ -57,9 +56,8 @@ EglOnXBackend::EglOnXBackend()
|
||||||
|
|
||||||
#if HAVE_X11_XCB
|
#if HAVE_X11_XCB
|
||||||
EglOnXBackend::EglOnXBackend(X11WindowedBackend *backend)
|
EglOnXBackend::EglOnXBackend(X11WindowedBackend *backend)
|
||||||
: OpenGLBackend()
|
: AbstractEglBackend()
|
||||||
, m_overlayWindow(nullptr)
|
, m_overlayWindow(nullptr)
|
||||||
, ctx(EGL_NO_CONTEXT)
|
|
||||||
, surfaceHasSubPost(0)
|
, surfaceHasSubPost(0)
|
||||||
, m_bufferAge(0)
|
, m_bufferAge(0)
|
||||||
, m_usesOverlayWindow(false)
|
, m_usesOverlayWindow(false)
|
||||||
|
@ -81,12 +79,7 @@ EglOnXBackend::~EglOnXBackend()
|
||||||
if (isFailed() && m_overlayWindow) {
|
if (isFailed() && m_overlayWindow) {
|
||||||
m_overlayWindow->destroy();
|
m_overlayWindow->destroy();
|
||||||
}
|
}
|
||||||
cleanupGL();
|
cleanup();
|
||||||
doneCurrent();
|
|
||||||
eglDestroyContext(dpy, ctx);
|
|
||||||
eglDestroySurface(dpy, surface);
|
|
||||||
eglTerminate(dpy);
|
|
||||||
eglReleaseThread();
|
|
||||||
if (m_overlayWindow) {
|
if (m_overlayWindow) {
|
||||||
if (overlayWindow()->window()) {
|
if (overlayWindow()->window()) {
|
||||||
overlayWindow()->destroy();
|
overlayWindow()->destroy();
|
||||||
|
@ -105,22 +98,13 @@ void EglOnXBackend::init()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
initEGL();
|
initKWinGL();
|
||||||
if (!hasGLExtension(QByteArrayLiteral("EGL_KHR_image")) &&
|
if (!hasGLExtension(QByteArrayLiteral("EGL_KHR_image")) &&
|
||||||
(!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_base")) ||
|
(!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_base")) ||
|
||||||
!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_pixmap")))) {
|
!hasGLExtension(QByteArrayLiteral("EGL_KHR_image_pixmap")))) {
|
||||||
setFailed(QStringLiteral("Required support for binding pixmaps to EGLImages not found, disabling compositing"));
|
setFailed(QStringLiteral("Required support for binding pixmaps to EGLImages not found, disabling compositing"));
|
||||||
return;
|
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"))) {
|
if (!hasGLExtension(QByteArrayLiteral("GL_OES_EGL_image"))) {
|
||||||
setFailed(QStringLiteral("Required extension GL_OES_EGL_image not found, disabling compositing"));
|
setFailed(QStringLiteral("Required extension GL_OES_EGL_image not found, disabling compositing"));
|
||||||
return;
|
return;
|
||||||
|
@ -128,7 +112,7 @@ void EglOnXBackend::init()
|
||||||
|
|
||||||
// check for EGL_NV_post_sub_buffer and whether it can be used on the surface
|
// 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 (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();
|
EGLint error = eglGetError();
|
||||||
if (error != EGL_SUCCESS && error != EGL_BAD_ATTRIBUTE) {
|
if (error != EGL_SUCCESS && error != EGL_BAD_ATTRIBUTE) {
|
||||||
setFailed(QStringLiteral("query surface failed"));
|
setFailed(QStringLiteral("query surface failed"));
|
||||||
|
@ -139,14 +123,7 @@ void EglOnXBackend::init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSupportsBufferAge(false);
|
initBufferAge();
|
||||||
|
|
||||||
if (hasGLExtension(QByteArrayLiteral("EGL_EXT_buffer_age"))) {
|
|
||||||
const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
|
|
||||||
|
|
||||||
if (useBufferAge != "0")
|
|
||||||
setSupportsBufferAge(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSyncsToVBlank(false);
|
setSyncsToVBlank(false);
|
||||||
setBlocksForRetrace(false);
|
setBlocksForRetrace(false);
|
||||||
|
@ -158,9 +135,9 @@ void EglOnXBackend::init()
|
||||||
if (options->glPreferBufferSwap() != Options::NoSwapEncourage) {
|
if (options->glPreferBufferSwap() != Options::NoSwapEncourage) {
|
||||||
// check if swap interval 1 is supported
|
// check if swap interval 1 is supported
|
||||||
EGLint val;
|
EGLint val;
|
||||||
eglGetConfigAttrib(dpy, config, EGL_MAX_SWAP_INTERVAL, &val);
|
eglGetConfigAttrib(eglDisplay(), config(), EGL_MAX_SWAP_INTERVAL, &val);
|
||||||
if (val >= 1) {
|
if (val >= 1) {
|
||||||
if (eglSwapInterval(dpy, 1)) {
|
if (eglSwapInterval(eglDisplay(), 1)) {
|
||||||
qCDebug(KWIN_CORE) << "Enabled v-sync";
|
qCDebug(KWIN_CORE) << "Enabled v-sync";
|
||||||
setSyncsToVBlank(true);
|
setSyncsToVBlank(true);
|
||||||
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
|
const QByteArray tripleBuffer = qgetenv("KWIN_TRIPLE_BUFFER");
|
||||||
|
@ -175,7 +152,7 @@ void EglOnXBackend::init()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// disable v-sync
|
// disable v-sync
|
||||||
eglSwapInterval(dpy, 0);
|
eglSwapInterval(eglDisplay(), 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* In the GLX backend, we fall back to using glCopyPixels if we have no extension providing support for partial screen updates.
|
/* 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),
|
* 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. */
|
* 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";
|
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()
|
bool EglOnXBackend::initRenderingContext()
|
||||||
{
|
{
|
||||||
// Get the list of client extensions
|
initClientExtensions();
|
||||||
const QByteArray clientExtensionString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
EGLDisplay dpy;
|
||||||
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(' ');
|
|
||||||
|
|
||||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||||
// if the implementation supports it.
|
// 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) {
|
if (havePlatformBase) {
|
||||||
// Make sure that the X11 platform is supported
|
// Make sure that the X11 platform is supported
|
||||||
if (!clientExtensions.contains("EGL_EXT_platform_x11"))
|
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_x11")))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const int attribs[] = {
|
const int attribs[] = {
|
||||||
|
@ -220,19 +192,8 @@ bool EglOnXBackend::initRenderingContext()
|
||||||
|
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
return false;
|
return false;
|
||||||
|
setEglDisplay(dpy);
|
||||||
EGLint major, minor;
|
initEglAPI();
|
||||||
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
|
|
||||||
|
|
||||||
initBufferConfigs();
|
initBufferConfigs();
|
||||||
|
|
||||||
|
@ -258,23 +219,30 @@ bool EglOnXBackend::initRenderingContext()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EGLSurface surface = EGL_NO_SURFACE;
|
||||||
if (havePlatformBase) {
|
if (havePlatformBase) {
|
||||||
// Note: Window is 64 bits on a 64-bit architecture whereas xcb_window_t is
|
// Note: Window is 64 bits on a 64-bit architecture whereas xcb_window_t is
|
||||||
// always 32 bits. eglCreatePlatformWindowSurfaceEXT() expects the
|
// always 32 bits. eglCreatePlatformWindowSurfaceEXT() expects the
|
||||||
// native_window parameter to be pointer to a Window, so this variable
|
// native_window parameter to be pointer to a Window, so this variable
|
||||||
// cannot be an xcb_window_t.
|
// cannot be an xcb_window_t.
|
||||||
surface = eglCreatePlatformWindowSurfaceEXT(dpy, config, (void *) &window, nullptr);
|
surface = eglCreatePlatformWindowSurfaceEXT(dpy, config(), (void *) &window, nullptr);
|
||||||
} else {
|
} 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
|
#ifdef KWIN_HAVE_OPENGLES
|
||||||
const EGLint context_attribs[] = {
|
const EGLint context_attribs[] = {
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs);
|
ctx = eglCreateContext(dpy, config(), EGL_NO_CONTEXT, context_attribs);
|
||||||
#else
|
#else
|
||||||
const EGLint context_attribs_31_core[] = {
|
const EGLint context_attribs_31_core[] = {
|
||||||
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
|
||||||
|
@ -291,24 +259,23 @@ bool EglOnXBackend::initRenderingContext()
|
||||||
|
|
||||||
// Try to create a 3.1 core context
|
// Try to create a 3.1 core context
|
||||||
if (options->glCoreProfile() && extensions.contains("EGL_KHR_create_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)
|
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
|
#endif
|
||||||
|
|
||||||
if (ctx == EGL_NO_CONTEXT) {
|
if (ctx == EGL_NO_CONTEXT) {
|
||||||
qCCritical(KWIN_CORE) << "Create Context failed";
|
qCCritical(KWIN_CORE) << "Create Context failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setContext(ctx);
|
||||||
|
|
||||||
if (eglMakeCurrent(dpy, surface, surface, ctx) == EGL_FALSE) {
|
if (eglMakeCurrent(dpy, surface, surface, ctx) == EGL_FALSE) {
|
||||||
qCCritical(KWIN_CORE) << "Make Context Current failed";
|
qCCritical(KWIN_CORE) << "Make Context Current failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(KWIN_CORE) << "EGL version: " << major << "." << minor;
|
|
||||||
|
|
||||||
EGLint error = eglGetError();
|
EGLint error = eglGetError();
|
||||||
if (error != EGL_SUCCESS) {
|
if (error != EGL_SUCCESS) {
|
||||||
qCWarning(KWIN_CORE) << "Error occurred while creating context " << error;
|
qCWarning(KWIN_CORE) << "Error occurred while creating context " << error;
|
||||||
|
@ -337,7 +304,7 @@ bool EglOnXBackend::initBufferConfigs()
|
||||||
|
|
||||||
EGLint count;
|
EGLint count;
|
||||||
EGLConfig configs[1024];
|
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";
|
qCCritical(KWIN_CORE) << "choose config failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -350,14 +317,14 @@ bool EglOnXBackend::initBufferConfigs()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
config = configs[0];
|
setConfig(configs[0]);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
EGLint val;
|
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";
|
qCCritical(KWIN_CORE) << "egl get config attrib failed";
|
||||||
}
|
}
|
||||||
if (uint32_t(val) == attribs->visual) {
|
if (uint32_t(val) == attribs->visual) {
|
||||||
config = configs[i];
|
setConfig(configs[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +346,7 @@ void EglOnXBackend::present()
|
||||||
m_swapProfiler.begin();
|
m_swapProfiler.begin();
|
||||||
}
|
}
|
||||||
// the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation)
|
// 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) {
|
if (gs_tripleBufferNeedsDetection) {
|
||||||
eglWaitGL();
|
eglWaitGL();
|
||||||
if (char result = m_swapProfiler.end()) {
|
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
|
// TODO this is a workaround, we should get __GL_YIELD set before libGL checks it
|
||||||
if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) {
|
if (qstrcmp(qgetenv("__GL_YIELD"), "USLEEP")) {
|
||||||
options->setGlPreferBufferSwap(0);
|
options->setGlPreferBufferSwap(0);
|
||||||
eglSwapInterval(dpy, 0);
|
eglSwapInterval(eglDisplay(), 0);
|
||||||
qCWarning(KWIN_CORE) << "\nIt seems you are using the nvidia driver without triple buffering\n"
|
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"
|
"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"
|
"Preferably, enable the TripleBuffer Option in the xorg.conf Device\n"
|
||||||
|
@ -400,12 +367,12 @@ void EglOnXBackend::present()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (supportsBufferAge()) {
|
if (supportsBufferAge()) {
|
||||||
eglQuerySurface(dpy, surface, EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
eglQuerySurface(eglDisplay(), surface(), EGL_BUFFER_AGE_EXT, &m_bufferAge);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// a part of the screen changed, and we can use eglPostSubBufferNV to copy the updated area
|
// a part of the screen changed, and we can use eglPostSubBufferNV to copy the updated area
|
||||||
foreach (const QRect & r, lastDamage().rects()) {
|
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);
|
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
|
bool EglOnXBackend::usesOverlayWindow() const
|
||||||
{
|
{
|
||||||
return m_usesOverlayWindow;
|
return m_usesOverlayWindow;
|
||||||
|
@ -524,60 +476,11 @@ OverlayWindow* EglOnXBackend::overlayWindow()
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|
||||||
EglTexture::EglTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglOnXBackend *backend)
|
EglTexture::EglTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglOnXBackend *backend)
|
||||||
: SceneOpenGL::TexturePrivate()
|
: AbstractEglTexture(texture, backend)
|
||||||
, q(texture)
|
|
||||||
, m_backend(backend)
|
|
||||||
, m_image(EGL_NO_IMAGE_KHR)
|
|
||||||
{
|
{
|
||||||
m_target = GL_TEXTURE_2D;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EglTexture::~EglTexture()
|
EglTexture::~EglTexture() = default;
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
void KWin::EglTexture::onDamage()
|
void KWin::EglTexture::onDamage()
|
||||||
{
|
{
|
||||||
|
@ -585,7 +488,7 @@ void KWin::EglTexture::onDamage()
|
||||||
// This is just implemented to be consistent with
|
// This is just implemented to be consistent with
|
||||||
// the example in mesa/demos/src/egl/opengles1/texture_from_pixmap.c
|
// the example in mesa/demos/src/egl/opengles1/texture_from_pixmap.c
|
||||||
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
|
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) m_image);
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) image());
|
||||||
}
|
}
|
||||||
GLTexturePrivate::onDamage();
|
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
|
#ifndef KWIN_EGL_ON_X_BACKEND_H
|
||||||
#define KWIN_EGL_ON_X_BACKEND_H
|
#define KWIN_EGL_ON_X_BACKEND_H
|
||||||
|
#include "abstract_egl_backend.h"
|
||||||
#include "scene_opengl.h"
|
#include "scene_opengl.h"
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
|
@ -29,7 +30,7 @@ class X11WindowedBackend;
|
||||||
/**
|
/**
|
||||||
* @brief OpenGL Backend using Egl windowing system over an X overlay window.
|
* @brief OpenGL Backend using Egl windowing system over an X overlay window.
|
||||||
**/
|
**/
|
||||||
class EglOnXBackend : public OpenGLBackend
|
class EglOnXBackend : public AbstractEglBackend
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EglOnXBackend();
|
EglOnXBackend();
|
||||||
|
@ -41,8 +42,6 @@ public:
|
||||||
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
|
||||||
virtual QRegion prepareRenderingFrame();
|
virtual QRegion prepareRenderingFrame();
|
||||||
virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion);
|
virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion);
|
||||||
virtual bool makeCurrent() override;
|
|
||||||
virtual void doneCurrent() override;
|
|
||||||
virtual OverlayWindow* overlayWindow() override;
|
virtual OverlayWindow* overlayWindow() override;
|
||||||
virtual bool usesOverlayWindow() const override;
|
virtual bool usesOverlayWindow() const override;
|
||||||
|
|
||||||
|
@ -57,10 +56,6 @@ private:
|
||||||
* @brief The OverlayWindow used by this Backend.
|
* @brief The OverlayWindow used by this Backend.
|
||||||
**/
|
**/
|
||||||
OverlayWindow *m_overlayWindow;
|
OverlayWindow *m_overlayWindow;
|
||||||
EGLDisplay dpy;
|
|
||||||
EGLConfig config;
|
|
||||||
EGLSurface surface;
|
|
||||||
EGLContext ctx;
|
|
||||||
int surfaceHasSubPost;
|
int surfaceHasSubPost;
|
||||||
int m_bufferAge;
|
int m_bufferAge;
|
||||||
bool m_usesOverlayWindow;
|
bool m_usesOverlayWindow;
|
||||||
|
@ -77,21 +72,15 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief Texture using an EGLImageKHR.
|
* @brief Texture using an EGLImageKHR.
|
||||||
**/
|
**/
|
||||||
class EglTexture : public SceneOpenGL::TexturePrivate
|
class EglTexture : public AbstractEglTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~EglTexture();
|
virtual ~EglTexture();
|
||||||
virtual void onDamage();
|
virtual void onDamage();
|
||||||
virtual bool loadTexture(WindowPixmap *pixmap) override;
|
|
||||||
virtual OpenGLBackend *backend();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class EglOnXBackend;
|
friend class EglOnXBackend;
|
||||||
EglTexture(SceneOpenGL::Texture *texture, EglOnXBackend *backend);
|
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
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue