kwin/scene_opengl_egl.cpp

299 lines
8.4 KiB
C++
Raw Normal View History

2010-11-21 13:01:39 +00:00
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
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/>.
*********************************************************************/
// This file is included in scene_opengl.cpp
//#include "scene_opengl.h"
#include <QX11Info>
EGLDisplay dpy;
EGLConfig config;
EGLSurface surface;
EGLContext ctx;
int surfaceHasSubPost;
2010-11-21 13:01:39 +00:00
2011-01-30 14:34:42 +00:00
SceneOpenGL::SceneOpenGL(Workspace* ws)
: Scene(ws)
, init_ok(false)
{
if (!initRenderingContext())
return;
2010-11-21 13:01:39 +00:00
initEGL();
if (!hasGLExtension("EGL_KHR_image") &&
(!hasGLExtension("EGL_KHR_image_base") ||
!hasGLExtension("EGL_KHR_image_pixmap"))) {
kError(1212) << "Required support for binding pixmaps to EGLImages not found, disabling compositing";
return;
}
initGL();
if (!hasGLExtension("GL_OES_EGL_image")) {
kError(1212) << "Required extension GL_OES_EGL_image not found, disabling compositing";
return;
}
2011-01-30 14:34:42 +00:00
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
2010-12-11 09:57:29 +00:00
if (!ShaderManager::instance()->isValid()) {
2011-01-30 14:34:42 +00:00
kError(1212) << "Shaders not valid, ES compositing not possible";
2010-11-21 13:01:39 +00:00
return;
}
2010-12-11 09:57:29 +00:00
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
2010-11-21 13:01:39 +00:00
2011-01-30 14:34:42 +00:00
if (checkGLError("Init")) {
kError(1212) << "OpenGL compositing setup failed";
2010-11-21 13:01:39 +00:00
return; // error
}
2011-01-30 14:34:42 +00:00
init_ok = true;
}
2010-11-21 13:01:39 +00:00
SceneOpenGL::~SceneOpenGL()
2011-01-30 14:34:42 +00:00
{
if (!init_ok) {
// TODO this probably needs to clean up whatever has been created until the failure
m_overlayWindow->destroy();
return;
}
2011-01-30 14:34:42 +00:00
foreach (Window * w, windows)
delete w;
// do cleanup after initBuffer()
cleanupGL();
2011-01-30 14:34:42 +00:00
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(dpy, ctx);
eglDestroySurface(dpy, surface);
eglTerminate(dpy);
eglReleaseThread();
2010-11-21 13:01:39 +00:00
SceneOpenGL::EffectFrame::cleanup();
2011-01-30 14:34:42 +00:00
checkGLError("Cleanup");
if (m_overlayWindow->window()) {
m_overlayWindow->destroy();
}
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
bool SceneOpenGL::initTfp()
2011-01-30 14:34:42 +00:00
{
2010-11-21 13:01:39 +00:00
return false;
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
bool SceneOpenGL::initRenderingContext()
2011-01-30 14:34:42 +00:00
{
dpy = eglGetDisplay(display());
if (dpy == EGL_NO_DISPLAY)
return false;
EGLint major, minor;
2011-01-30 14:34:42 +00:00
if (eglInitialize(dpy, &major, &minor) == EGL_FALSE)
return false;
2011-01-30 14:34:42 +00:00
eglBindAPI(EGL_OPENGL_ES_API);
initBufferConfigs();
if (!m_overlayWindow->create()) {
2011-01-30 14:34:42 +00:00
kError(1212) << "Could not get overlay window";
return false;
} else {
m_overlayWindow->setup(None);
2011-01-30 14:34:42 +00:00
}
surface = eglCreateWindowSurface(dpy, config, m_overlayWindow->window(), 0);
eglSurfaceAttrib(dpy, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
eglQuerySurface(dpy, surface, EGL_POST_SUB_BUFFER_SUPPORTED_NV, &surfaceHasSubPost);
const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
2011-01-30 14:34:42 +00:00
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs);
if (ctx == EGL_NO_CONTEXT)
return false;
2011-01-30 14:34:42 +00:00
if (eglMakeCurrent(dpy, surface, surface, ctx) == EGL_FALSE)
return false;
2011-01-30 14:34:42 +00:00
kDebug(1212) << "EGL version: " << major << "." << minor;
EGLint error = eglGetError();
2011-01-30 14:34:42 +00:00
if (error != EGL_SUCCESS) {
kWarning(1212) << "Error occurred while creating context " << error;
return false;
2010-11-21 13:01:39 +00:00
}
2011-01-30 14:34:42 +00:00
return true;
}
2010-11-21 13:01:39 +00:00
bool SceneOpenGL::initBuffer()
2011-01-30 14:34:42 +00:00
{
2010-11-21 13:01:39 +00:00
return false;
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
bool SceneOpenGL::initBufferConfigs()
2011-01-30 14:34:42 +00:00
{
const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE,
};
EGLint count;
EGLConfig configs[1024];
eglChooseConfig(dpy, config_attribs, configs, 1024, &count);
EGLint visualId = XVisualIDFromVisual((Visual*)QX11Info::appVisual());
config = configs[0];
2011-01-30 14:34:42 +00:00
for (int i = 0; i < count; i++) {
EGLint val;
eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &val);
2011-01-30 14:34:42 +00:00
if (visualId == val) {
config = configs[i];
break;
}
2010-11-21 13:01:39 +00:00
}
2011-01-30 14:34:42 +00:00
return true;
}
2010-11-21 13:01:39 +00:00
bool SceneOpenGL::initDrawableConfigs()
2011-01-30 14:34:42 +00:00
{
2010-11-21 13:01:39 +00:00
return false;
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
// the entry function for painting
2011-01-30 14:34:42 +00:00
void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
QElapsedTimer renderTimer;
renderTimer.start();
2011-01-30 14:34:42 +00:00
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
}
grabXServer();
XSync(display(), false);
2010-11-21 13:01:39 +00:00
int mask = 0;
2011-01-30 14:34:42 +00:00
paintScreen(&mask, &damage); // call generic implementation
ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
if (m_overlayWindow->window()) // show the window only after the first pass, since
m_overlayWindow->show(); // that pass may take long
lastRenderTime = renderTimer.elapsed();
if (!damage.isEmpty()) {
flushBuffer(mask, damage);
}
2010-11-21 13:01:39 +00:00
// do cleanup
stacking_order.clear();
2011-01-30 14:34:42 +00:00
checkGLError("PostPaint");
}
2010-11-21 13:01:39 +00:00
void SceneOpenGL::waitSync()
2011-01-30 14:34:42 +00:00
{
2010-11-21 13:01:39 +00:00
// not used in EGL
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
2011-01-30 14:34:42 +00:00
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
{
glFlush();
if (mask & PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV) {
QRect damageRect = damage.boundingRect();
eglPostSubBufferNV(dpy, surface, damageRect.left(), displayHeight() - damageRect.bottom() - 1, damageRect.width(), damageRect.height());
2011-01-30 14:34:42 +00:00
} else {
eglSwapBuffers(dpy, surface);
}
eglWaitGL();
// TODO: remove for wayland
2011-01-30 14:34:42 +00:00
XFlush(display());
}
2010-11-21 13:01:39 +00:00
void SceneOpenGL::screenGeometryChanged(const QSize &size)
{
glViewport(0,0, size.width(), size.height());
Scene::screenGeometryChanged(size);
ShaderManager::instance()->resetAllShaders();
}
2010-11-21 13:01:39 +00:00
//****************************************
// SceneOpenGL::Texture
//****************************************
SceneOpenGL::TexturePrivate::TexturePrivate()
2011-01-30 14:34:42 +00:00
{
m_target = GL_TEXTURE_2D;
m_image = EGL_NO_IMAGE_KHR;
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
SceneOpenGL::TexturePrivate::~TexturePrivate()
2011-01-30 14:34:42 +00:00
{
if (m_image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(dpy, m_image);
}
2011-01-30 14:34:42 +00:00
}
2010-11-21 13:01:39 +00:00
void SceneOpenGL::Texture::findTarget()
2011-01-30 14:34:42 +00:00
{
Q_D(Texture);
d->m_target = GL_TEXTURE_2D;
}
2011-01-30 14:34:42 +00:00
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
int depth, QRegion region)
{
// decrease the reference counter for the old texture
d_ptr = new TexturePrivate();
Q_D(Texture);
2010-12-05 08:17:38 +00:00
Q_UNUSED(depth)
Q_UNUSED(region)
if (pix == None)
return false;
glGenTextures(1, &d->m_texture);
setWrapMode(GL_CLAMP_TO_EDGE);
setFilter(GL_LINEAR);
bind();
const EGLint attribs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE
};
d->m_image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
(EGLClientBuffer)pix, attribs);
if (EGL_NO_IMAGE_KHR == d->m_image) {
kDebug(1212) << "failed to create egl image";
unbind();
discard();
return false;
2010-11-21 13:01:39 +00:00
}
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->m_image);
unbind();
checkGLError("load texture");
setYInverted(true);
d->m_size = size;
2011-01-30 14:34:42 +00:00
return true;
}
2010-11-21 13:01:39 +00:00
void SceneOpenGL::TexturePrivate::onDamage()
2011-01-30 14:34:42 +00:00
{
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) m_image);
}
GLTexturePrivate::onDamage();
}