You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
8.4 KiB
298 lines
8.4 KiB
/******************************************************************** |
|
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->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(); |
|
}
|
|
|