298 lines
8.4 KiB
C++
298 lines
8.4 KiB
C++
/********************************************************************
|
|
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;
|
|
|
|
SceneOpenGL::SceneOpenGL(Workspace* ws)
|
|
: Scene(ws)
|
|
, init_ok(false)
|
|
{
|
|
if (!initRenderingContext())
|
|
return;
|
|
|
|
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;
|
|
}
|
|
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
|
|
if (!ShaderManager::instance()->isValid()) {
|
|
kError(1212) << "Shaders not valid, ES compositing not possible";
|
|
return;
|
|
}
|
|
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
|
|
|
|
if (checkGLError("Init")) {
|
|
kError(1212) << "OpenGL compositing setup failed";
|
|
return; // error
|
|
}
|
|
init_ok = true;
|
|
}
|
|
|
|
SceneOpenGL::~SceneOpenGL()
|
|
{
|
|
if (!init_ok) {
|
|
// TODO this probably needs to clean up whatever has been created until the failure
|
|
m_overlayWindow->destroy();
|
|
return;
|
|
}
|
|
foreach (Window * w, windows)
|
|
delete w;
|
|
// do cleanup after initBuffer()
|
|
cleanupGL();
|
|
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
eglDestroyContext(dpy, ctx);
|
|
eglDestroySurface(dpy, surface);
|
|
eglTerminate(dpy);
|
|
eglReleaseThread();
|
|
SceneOpenGL::EffectFrame::cleanup();
|
|
checkGLError("Cleanup");
|
|
if (m_overlayWindow->window()) {
|
|
m_overlayWindow->destroy();
|
|
}
|
|
}
|
|
|
|
bool SceneOpenGL::initTfp()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool SceneOpenGL::initRenderingContext()
|
|
{
|
|
dpy = eglGetDisplay(display());
|
|
if (dpy == EGL_NO_DISPLAY)
|
|
return false;
|
|
EGLint major, minor;
|
|
if (eglInitialize(dpy, &major, &minor) == EGL_FALSE)
|
|
return false;
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
initBufferConfigs();
|
|
if (!m_overlayWindow->create()) {
|
|
kError(1212) << "Could not get overlay window";
|
|
return false;
|
|
} else {
|
|
m_overlayWindow->setup(None);
|
|
}
|
|
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
|
|
};
|
|
|
|
ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, context_attribs);
|
|
if (ctx == EGL_NO_CONTEXT)
|
|
return false;
|
|
if (eglMakeCurrent(dpy, surface, surface, ctx) == EGL_FALSE)
|
|
return false;
|
|
kDebug(1212) << "EGL version: " << major << "." << minor;
|
|
EGLint error = eglGetError();
|
|
if (error != EGL_SUCCESS) {
|
|
kWarning(1212) << "Error occurred while creating context " << error;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SceneOpenGL::initBuffer()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool SceneOpenGL::initBufferConfigs()
|
|
{
|
|
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];
|
|
for (int i = 0; i < count; i++) {
|
|
EGLint val;
|
|
eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &val);
|
|
if (visualId == val) {
|
|
config = configs[i];
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SceneOpenGL::initDrawableConfigs()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// the entry function for painting
|
|
void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
|
|
{
|
|
QElapsedTimer renderTimer;
|
|
renderTimer.start();
|
|
|
|
foreach (Toplevel * c, toplevels) {
|
|
assert(windows.contains(c));
|
|
stacking_order.append(windows[ c ]);
|
|
}
|
|
|
|
grabXServer();
|
|
XSync(display(), false);
|
|
int mask = 0;
|
|
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);
|
|
}
|
|
// do cleanup
|
|
stacking_order.clear();
|
|
checkGLError("PostPaint");
|
|
}
|
|
|
|
void SceneOpenGL::waitSync()
|
|
{
|
|
// not used in EGL
|
|
}
|
|
|
|
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());
|
|
} else {
|
|
eglSwapBuffers(dpy, surface);
|
|
}
|
|
eglWaitGL();
|
|
// TODO: remove for wayland
|
|
XFlush(display());
|
|
}
|
|
|
|
void SceneOpenGL::screenGeometryChanged(const QSize &size)
|
|
{
|
|
glViewport(0,0, size.width(), size.height());
|
|
Scene::screenGeometryChanged(size);
|
|
ShaderManager::instance()->resetAllShaders();
|
|
}
|
|
|
|
//****************************************
|
|
// SceneOpenGL::Texture
|
|
//****************************************
|
|
|
|
SceneOpenGL::TexturePrivate::TexturePrivate()
|
|
{
|
|
m_target = GL_TEXTURE_2D;
|
|
m_image = EGL_NO_IMAGE_KHR;
|
|
}
|
|
|
|
SceneOpenGL::TexturePrivate::~TexturePrivate()
|
|
{
|
|
if (m_image != EGL_NO_IMAGE_KHR) {
|
|
eglDestroyImageKHR(dpy, m_image);
|
|
}
|
|
}
|
|
|
|
void SceneOpenGL::Texture::findTarget()
|
|
{
|
|
Q_D(Texture);
|
|
d->m_target = GL_TEXTURE_2D;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->m_image);
|
|
unbind();
|
|
checkGLError("load texture");
|
|
setYInverted(true);
|
|
d->m_size = size;
|
|
return true;
|
|
}
|
|
|
|
void SceneOpenGL::TexturePrivate::onDamage()
|
|
{
|
|
if (options->glStrictBinding) {
|
|
// 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();
|
|
}
|