kwin/plugins/platforms/x11/standalone/eglbackend.cpp
Vlad Zahorodnii ef6fa25d90 platforms/x11: Split out the EGL for X11 backend
EGL for X and EGL for Wayland backends are quite different. The main
motivation behind this change is to prepare the EGL backends for
monitoring vblank events. Things work quite differently depending on
if the EGL backend renders onto a toplevel window or overlay window.
2021-01-06 16:59:29 +00:00

160 lines
4.5 KiB
C++

/*
SPDX-FileCopyrightText: 2010, 2012 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "eglbackend.h"
#include "options.h"
#include "overlaywindow.h"
#include "scene.h"
#include "screens.h"
namespace KWin
{
EglBackend::EglBackend(Display *display)
: EglOnXBackend(display)
{
}
SceneOpenGLTexturePrivate *EglBackend::createBackendTexture(SceneOpenGLTexture *texture)
{
return new EglTexture(texture, this);
}
void EglBackend::screenGeometryChanged(const QSize &size)
{
Q_UNUSED(size)
// TODO: base implementation in OpenGLBackend
// The back buffer contents are now undefined
m_bufferAge = 0;
}
QRegion EglBackend::beginFrame(int screenId)
{
Q_UNUSED(screenId)
QRegion repaint;
if (supportsBufferAge())
repaint = accumulatedDamageHistory(m_bufferAge);
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
return repaint;
}
void EglBackend::endFrame(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion)
{
Q_UNUSED(screenId)
if (damagedRegion.isEmpty()) {
// If the damaged region of a window is fully occluded, the only
// rendering done, if any, will have been to repair a reused back
// buffer, making it identical to the front buffer.
//
// In this case we won't post the back buffer. Instead we'll just
// set the buffer age to 1, so the repaired regions won't be
// rendered again in the next frame.
if (!renderedRegion.isEmpty())
glFlush();
m_bufferAge = 1;
return;
}
presentSurface(surface(), renderedRegion, screens()->geometry());
if (overlayWindow() && overlayWindow()->window()) { // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long
}
// Save the damaged region to history
if (supportsBufferAge()) {
addToDamageHistory(damagedRegion);
}
}
void EglBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry)
{
if (damage.isEmpty()) {
return;
}
const bool fullRepaint = supportsBufferAge() || (damage == screenGeometry);
if (fullRepaint || !havePostSubBuffer()) {
// the entire screen changed, or we cannot do partial updates (which implies we enabled surface preservation)
eglSwapBuffers(eglDisplay(), surface);
if (supportsBufferAge()) {
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
for (const QRect &r : damage) {
eglPostSubBufferNV(eglDisplay(), surface, r.left(), screenGeometry.height() - r.bottom() - 1, r.width(), r.height());
}
}
}
/************************************************
* EglTexture
************************************************/
EglTexture::EglTexture(KWin::SceneOpenGLTexture *texture, EglBackend *backend)
: AbstractEglTexture(texture, backend)
, m_backend(backend)
{
}
EglTexture::~EglTexture()
{
}
bool EglTexture::loadTexture(WindowPixmap *pixmap)
{
const xcb_pixmap_t nativePixmap = pixmap->pixmap();
if (nativePixmap == XCB_NONE) {
return false;
}
glGenTextures(1, &m_texture);
auto q = texture();
q->setWrapMode(GL_CLAMP_TO_EDGE);
q->setFilter(GL_LINEAR);
q->bind();
const EGLint attribs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE
};
setImage(eglCreateImageKHR(m_backend->eglDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
(EGLClientBuffer)nativePixmap, attribs));
if (EGL_NO_IMAGE_KHR == image()) {
qCDebug(KWIN_CORE) << "failed to create egl image";
q->unbind();
q->discard();
return false;
}
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image());
q->unbind();
q->setYInverted(true);
m_size = pixmap->toplevel()->bufferGeometry().size();
updateMatrix();
return true;
}
void EglTexture::onDamage()
{
if (options->isGlStrictBinding()) {
// 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) image());
}
GLTexturePrivate::onDamage();
}
} // namespace KWin