kwin/scene_opengl_glx.cpp

800 lines
30 KiB
C++
Raw Normal View History

2010-11-21 16:01:39 +03:00
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Based on glcompmgr code by Felix Bellaby.
Using code from Compiz and Beryl.
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
// the configs used for the destination
GLXFBConfig SceneOpenGL::fbcbuffer_db;
GLXFBConfig SceneOpenGL::fbcbuffer_nondb;
// the configs used for windows
SceneOpenGL::FBConfigInfo SceneOpenGL::fbcdrawableinfo[ 32 + 1 ];
// GLX content
GLXContext SceneOpenGL::ctxbuffer;
GLXContext SceneOpenGL::ctxdrawable;
// the destination drawable where the compositing is done
GLXDrawable SceneOpenGL::glxbuffer = None;
GLXDrawable SceneOpenGL::last_pixmap = None;
2011-01-30 17:34:42 +03:00
SceneOpenGL::SceneOpenGL(Workspace* ws)
: Scene(ws)
, init_ok(false)
, selfCheckDone(false)
{
if (!Extensions::glxAvailable()) {
kDebug(1212) << "No glx extensions available";
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
initGLX();
// check for FBConfig support
2011-01-30 17:34:42 +03:00
if (!hasGLExtension("GLX_SGIX_fbconfig") || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
2010-11-21 16:01:39 +03:00
!glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap ||
2011-01-30 17:34:42 +03:00
!glXCreateWindow || !glXDestroyWindow) {
kError(1212) << "GLX_SGIX_fbconfig or required GLX functions missing";
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
}
if (!selectMode())
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
if (!initBuffer()) // create destination buffer
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
if (!initRenderingContext())
2010-11-21 16:01:39 +03:00
return; // error
// Initialize OpenGL
initGL();
2011-01-30 17:34:42 +03:00
if (QString((const char*)glGetString(GL_RENDERER)) == "Software Rasterizer") {
kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender.";
QTimer::singleShot(0, Workspace::self(), SLOT(fallbackToXRenderCompositing()));
2010-11-21 16:01:39 +03:00
return;
2011-01-30 17:34:42 +03:00
}
if (!hasGLExtension("GL_ARB_texture_non_power_of_two")
&& !hasGLExtension("GL_ARB_texture_rectangle")) {
kError(1212) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
}
if (db)
glDrawBuffer(GL_BACK);
2010-11-21 16:01:39 +03:00
// Check whether certain features are supported
has_waitSync = false;
2011-01-30 17:34:42 +03:00
if (glXGetVideoSync && glXIsDirect(display(), ctxbuffer) && options->glVSync) {
2010-11-21 16:01:39 +03:00
unsigned int sync;
2011-01-30 17:34:42 +03:00
if (glXGetVideoSync(&sync) == 0) {
if (glXWaitVideoSync(1, 0, &sync) == 0)
2010-11-21 16:01:39 +03:00
has_waitSync = true;
else
2011-01-30 17:34:42 +03:00
qWarning() << "NO VSYNC! glXWaitVideoSync(1,0,&uint) isn't 0 but" << glXWaitVideoSync(1, 0, &sync);
} else
qWarning() << "NO VSYNC! glXGetVideoSync(&uint) isn't 0 but" << glXGetVideoSync(&sync);
}
2010-11-21 16:01:39 +03:00
2011-01-30 17:34:42 +03:00
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
2010-11-21 16:01:39 +03:00
// scene shader setup
2011-01-30 17:34:42 +03:00
if (GLPlatform::instance()->supports(GLSL)) {
2010-12-11 12:57:29 +03:00
if (!ShaderManager::instance()->isValid()) {
kDebug(1212) << "No Scene Shaders available";
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
// OpenGL scene setup
2011-01-30 17:34:42 +03:00
glMatrixMode(GL_PROJECTION);
2010-11-21 16:01:39 +03:00
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
2011-01-30 17:34:42 +03:00
float ymax = zNear * tan(fovy * M_PI / 360.0f);
2010-11-21 16:01:39 +03:00
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
// swap top and bottom to have OpenGL coordinate system match X system
2011-01-30 17:34:42 +03:00
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
2010-11-21 16:01:39 +03:00
glLoadIdentity();
2011-01-30 17:34:42 +03:00
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
glTranslatef(xmin * scaleFactor, ymax * scaleFactor, -1.1);
glScalef((xmax - xmin)*scaleFactor / displayWidth(), -(ymax - ymin)*scaleFactor / displayHeight(), 0.001);
if (checkGLError("Init")) {
kError(1212) << "OpenGL compositing setup failed";
2010-11-21 16:01:39 +03:00
return; // error
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
// selfcheck is broken (see Bug 253357)
#if 0
// Do self-check immediatelly during compositing setup only when it's not KWin startup
// at the same time (in other words, only when activating compositing using the kcm).
// Currently selfcheck causes bad flicker (due to X mapping the overlay window
// for too long?) which looks bad during KDE startup.
2011-01-30 17:34:42 +03:00
if (!initting) {
if (!selfCheck())
2010-11-21 16:01:39 +03:00
return;
selfCheckDone = true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
#endif
2011-01-30 17:34:42 +03:00
kDebug(1212) << "DB:" << db << ", Direct:" << bool(glXIsDirect(display(), ctxbuffer)) << endl;
2010-11-21 16:01:39 +03:00
init_ok = true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
SceneOpenGL::~SceneOpenGL()
2011-01-30 17:34:42 +03:00
{
if (!init_ok) {
2010-11-21 16:01:39 +03:00
// TODO this probably needs to clean up whatever has been created until the failure
wspace->destroyOverlay();
return;
2011-01-30 17:34:42 +03:00
}
foreach (Window * w, windows)
delete w;
2010-11-21 16:01:39 +03:00
// do cleanup after initBuffer()
2011-01-30 17:34:42 +03:00
glXMakeCurrent(display(), None, NULL);
glXDestroyContext(display(), ctxbuffer);
if (wspace->overlayWindow()) {
if (hasGLXVersion(1, 3))
glXDestroyWindow(display(), glxbuffer);
XDestroyWindow(display(), buffer);
2010-11-21 16:01:39 +03:00
wspace->destroyOverlay();
2011-01-30 17:34:42 +03:00
} else {
glXDestroyPixmap(display(), glxbuffer);
XFreeGC(display(), gcroot);
XFreePixmap(display(), buffer);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
SceneOpenGL::EffectFrame::cleanup();
checkGLError("Cleanup");
}
2010-11-21 16:01:39 +03:00
bool SceneOpenGL::initTfp()
2011-01-30 17:34:42 +03:00
{
if (glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL)
2010-11-21 16:01:39 +03:00
return false;
return true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
bool SceneOpenGL::initRenderingContext()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
bool direct_rendering = options->glDirect;
KXErrorHandler errs1;
2011-01-30 17:34:42 +03:00
ctxbuffer = glXCreateNewContext(display(), fbcbuffer, GLX_RGBA_TYPE, NULL,
direct_rendering ? GL_TRUE : GL_FALSE);
bool failed = (ctxbuffer == NULL || !glXMakeCurrent(display(), glxbuffer, ctxbuffer));
if (errs1.error(true)) // always check for error( having it all in one if () could skip
2010-11-21 16:01:39 +03:00
failed = true; // it due to evaluation short-circuiting
2011-01-30 17:34:42 +03:00
if (failed) {
if (!direct_rendering) {
kDebug(1212).nospace() << "Couldn't initialize rendering context ("
<< KXErrorHandler::errorMessage(errs1.errorEvent()) << ")";
2010-11-21 16:01:39 +03:00
return false;
2011-01-30 17:34:42 +03:00
}
glXMakeCurrent(display(), None, NULL);
if (ctxbuffer != NULL)
glXDestroyContext(display(), ctxbuffer);
2010-11-21 16:01:39 +03:00
direct_rendering = false; // try again
KXErrorHandler errs2;
2011-01-30 17:34:42 +03:00
ctxbuffer = glXCreateNewContext(display(), fbcbuffer, GLX_RGBA_TYPE, NULL, GL_FALSE);
bool failed = (ctxbuffer == NULL || !glXMakeCurrent(display(), glxbuffer, ctxbuffer));
if (errs2.error(true))
2010-11-21 16:01:39 +03:00
failed = true;
2011-01-30 17:34:42 +03:00
if (failed) {
kDebug(1212).nospace() << "Couldn't initialize rendering context ("
<< KXErrorHandler::errorMessage(errs2.errorEvent()) << ")";
2010-11-21 16:01:39 +03:00
return false;
}
}
2011-01-30 17:34:42 +03:00
return true;
}
2010-11-21 16:01:39 +03:00
// create destination buffer
bool SceneOpenGL::initBuffer()
2011-01-30 17:34:42 +03:00
{
if (!initBufferConfigs())
2010-11-21 16:01:39 +03:00
return false;
2011-01-30 17:34:42 +03:00
if (fbcbuffer_db != NULL && wspace->createOverlay()) {
// we have overlay, try to create double-buffered window in it
2010-11-21 16:01:39 +03:00
fbcbuffer = fbcbuffer_db;
2011-01-30 17:34:42 +03:00
XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer);
2010-11-21 16:01:39 +03:00
XSetWindowAttributes attrs;
2011-01-30 17:34:42 +03:00
attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone);
buffer = XCreateWindow(display(), wspace->overlayWindow(), 0, 0, displayWidth(), displayHeight(),
0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs);
if (hasGLXVersion(1, 3))
glxbuffer = glXCreateWindow(display(), fbcbuffer, buffer, NULL);
2010-11-21 16:01:39 +03:00
else
glxbuffer = buffer;
2011-01-30 17:34:42 +03:00
wspace->setupOverlay(buffer);
2010-11-21 16:01:39 +03:00
db = true;
2011-01-30 17:34:42 +03:00
XFree(visual);
} else if (fbcbuffer_nondb != NULL) {
// cannot get any double-buffered drawable, will double-buffer using a pixmap
2010-11-21 16:01:39 +03:00
fbcbuffer = fbcbuffer_nondb;
2011-01-30 17:34:42 +03:00
XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer);
2010-11-21 16:01:39 +03:00
XGCValues gcattr;
gcattr.subwindow_mode = IncludeInferiors;
2011-01-30 17:34:42 +03:00
gcroot = XCreateGC(display(), rootWindow(), GCSubwindowMode, &gcattr);
buffer = XCreatePixmap(display(), rootWindow(), displayWidth(), displayHeight(),
visual->depth);
glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL);
2010-11-21 16:01:39 +03:00
db = false;
2011-01-30 17:34:42 +03:00
XFree(visual);
} else {
kError(1212) << "Couldn't create output buffer (failed to create overlay window?) !";
2010-11-21 16:01:39 +03:00
return false; // error
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
int vis_buffer;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbcbuffer, GLX_VISUAL_ID, &vis_buffer);
XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig(display(), fbcbuffer);
kDebug(1212) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number(vis_buffer, 16);
XFree(visinfo_buffer);
2010-11-21 16:01:39 +03:00
return true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
// choose the best configs for the destination buffer
bool SceneOpenGL::initBufferConfigs()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
int cnt;
2011-01-30 17:34:42 +03:00
GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt);
2010-11-21 16:01:39 +03:00
fbcbuffer_db = NULL;
fbcbuffer_nondb = NULL;
2011-01-30 17:34:42 +03:00
for (int i = 0; i < 2; i++) {
2010-11-21 16:01:39 +03:00
int back, stencil, depth, caveat, alpha;
back = i > 0 ? INT_MAX : 1;
stencil = INT_MAX;
depth = INT_MAX;
caveat = INT_MAX;
alpha = 0;
2011-01-30 17:34:42 +03:00
for (int j = 0; j < cnt; j++) {
2010-11-21 16:01:39 +03:00
XVisualInfo *vi;
int visual_depth;
2011-01-30 17:34:42 +03:00
vi = glXGetVisualFromFBConfig(display(), fbconfigs[ j ]);
if (vi == NULL)
2010-11-21 16:01:39 +03:00
continue;
visual_depth = vi->depth;
2011-01-30 17:34:42 +03:00
XFree(vi);
if (visual_depth != DefaultDepth(display(), DefaultScreen(display())))
2010-11-21 16:01:39 +03:00
continue;
int value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_ALPHA_SIZE, &alpha);
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BUFFER_SIZE, &value);
if (value != visual_depth && (value - alpha) != visual_depth)
2010-11-21 16:01:39 +03:00
continue;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_RENDER_TYPE, &value);
if (!(value & GLX_RGBA_BIT))
2010-11-21 16:01:39 +03:00
continue;
int back_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_DOUBLEBUFFER, &back_value);
if (i > 0) {
if (back_value > back)
2010-11-21 16:01:39 +03:00
continue;
2011-01-30 17:34:42 +03:00
} else {
if (back_value < back)
2010-11-21 16:01:39 +03:00
continue;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
int stencil_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_STENCIL_SIZE, &stencil_value);
if (stencil_value > stencil)
2010-11-21 16:01:39 +03:00
continue;
int depth_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_DEPTH_SIZE, &depth_value);
if (depth_value > depth)
2010-11-21 16:01:39 +03:00
continue;
int caveat_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_CONFIG_CAVEAT, &caveat_value);
if (caveat_value > caveat)
2010-11-21 16:01:39 +03:00
continue;
back = back_value;
stencil = stencil_value;
depth = depth_value;
caveat = caveat_value;
2011-01-30 17:34:42 +03:00
if (i > 0)
2010-11-21 16:01:39 +03:00
fbcbuffer_nondb = fbconfigs[ j ];
else
fbcbuffer_db = fbconfigs[ j ];
}
2011-01-30 17:34:42 +03:00
}
if (cnt)
XFree(fbconfigs);
if (fbcbuffer_db == NULL && fbcbuffer_nondb == NULL) {
kError(1212) << "Couldn't find framebuffer configuration for buffer!";
2010-11-21 16:01:39 +03:00
return false;
2011-01-30 17:34:42 +03:00
}
for (int i = 0; i <= 32; i++) {
if (fbcdrawableinfo[ i ].fbconfig == NULL)
2010-11-21 16:01:39 +03:00
continue;
int vis_drawable = 0;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbcdrawableinfo[ i ].fbconfig, GLX_VISUAL_ID, &vis_drawable);
kDebug(1212) << "Drawable visual (depth " << i << "): 0x" << QString::number(vis_drawable, 16);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
return true;
}
2010-11-21 16:01:39 +03:00
// make a list of the best configs for windows by depth
bool SceneOpenGL::initDrawableConfigs()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
int cnt;
2011-01-30 17:34:42 +03:00
GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt);
2010-11-21 16:01:39 +03:00
2011-01-30 17:34:42 +03:00
for (int i = 0; i <= 32; i++) {
2010-11-21 16:01:39 +03:00
int back, stencil, depth, caveat, alpha, mipmap, rgba;
back = INT_MAX;
stencil = INT_MAX;
depth = INT_MAX;
caveat = INT_MAX;
mipmap = 0;
rgba = 0;
fbcdrawableinfo[ i ].fbconfig = NULL;
fbcdrawableinfo[ i ].bind_texture_format = 0;
fbcdrawableinfo[ i ].texture_targets = 0;
fbcdrawableinfo[ i ].y_inverted = 0;
fbcdrawableinfo[ i ].mipmap = 0;
2011-01-30 17:34:42 +03:00
for (int j = 0; j < cnt; j++) {
2010-11-21 16:01:39 +03:00
XVisualInfo *vi;
int visual_depth;
2011-01-30 17:34:42 +03:00
vi = glXGetVisualFromFBConfig(display(), fbconfigs[ j ]);
if (vi == NULL)
2010-11-21 16:01:39 +03:00
continue;
visual_depth = vi->depth;
2011-01-30 17:34:42 +03:00
XFree(vi);
if (visual_depth != i)
2010-11-21 16:01:39 +03:00
continue;
int value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_ALPHA_SIZE, &alpha);
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BUFFER_SIZE, &value);
if (value != i && (value - alpha) != i)
2010-11-21 16:01:39 +03:00
continue;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_RENDER_TYPE, &value);
if (!(value & GLX_RGBA_BIT))
2010-11-21 16:01:39 +03:00
continue;
value = 0;
2011-01-30 17:34:42 +03:00
if (i == 32) {
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
if (value) {
// TODO I think this should be set only after the config passes all tests
rgba = 1;
fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT;
}
2011-01-30 17:34:42 +03:00
}
if (!value) {
if (rgba)
continue;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
if (!value)
continue;
fbcdrawableinfo[ i ].bind_texture_format = GLX_TEXTURE_FORMAT_RGB_EXT;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
int back_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_DOUBLEBUFFER, &back_value);
if (back_value > back)
2010-11-21 16:01:39 +03:00
continue;
int stencil_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_STENCIL_SIZE, &stencil_value);
if (stencil_value > stencil)
2010-11-21 16:01:39 +03:00
continue;
int depth_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_DEPTH_SIZE, &depth_value);
if (depth_value > depth)
2010-11-21 16:01:39 +03:00
continue;
int mipmap_value = -1;
2011-01-30 17:34:42 +03:00
if (GLTexture::framebufferObjectSupported()) {
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &mipmap_value);
if (mipmap_value < mipmap)
2010-11-21 16:01:39 +03:00
continue;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
int caveat_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_CONFIG_CAVEAT, &caveat_value);
if (caveat_value > caveat)
2010-11-21 16:01:39 +03:00
continue;
// ok, config passed all tests, it's the best one so far
fbcdrawableinfo[ i ].fbconfig = fbconfigs[ j ];
caveat = caveat_value;
back = back_value;
stencil = stencil_value;
depth = depth_value;
mipmap = mipmap_value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_BIND_TO_TEXTURE_TARGETS_EXT, &value);
fbcdrawableinfo[ i ].texture_targets = value;
2011-01-30 17:34:42 +03:00
glXGetFBConfigAttrib(display(), fbconfigs[ j ],
GLX_Y_INVERTED_EXT, &value);
2010-11-21 16:01:39 +03:00
fbcdrawableinfo[ i ].y_inverted = value;
fbcdrawableinfo[ i ].mipmap = mipmap;
}
2011-01-30 17:34:42 +03:00
}
if (cnt)
XFree(fbconfigs);
if (fbcdrawableinfo[ DefaultDepth(display(), DefaultScreen(display()))].fbconfig == NULL) {
kError(1212) << "Couldn't find framebuffer configuration for default depth!";
2010-11-21 16:01:39 +03:00
return false;
2011-01-30 17:34:42 +03:00
}
if (fbcdrawableinfo[ 32 ].fbconfig == NULL) {
kError(1212) << "Couldn't find framebuffer configuration for depth 32 (no ARGB GLX visual)!";
2010-11-21 16:01:39 +03:00
return false;
}
2011-01-30 17:34:42 +03:00
return true;
}
2010-11-21 16:01:39 +03:00
void SceneOpenGL::selfCheckSetup()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
KXErrorHandler err;
2011-01-30 17:34:42 +03:00
QImage img(selfCheckWidth(), selfCheckHeight(), QImage::Format_RGB32);
img.setPixel(0, 0, QColor(Qt::red).rgb());
img.setPixel(1, 0, QColor(Qt::green).rgb());
img.setPixel(2, 0, QColor(Qt::blue).rgb());
img.setPixel(0, 1, QColor(Qt::white).rgb());
img.setPixel(1, 1, QColor(Qt::black).rgb());
img.setPixel(2, 1, QColor(Qt::white).rgb());
QPixmap pix = QPixmap::fromImage(img);
foreach (const QPoint & p, selfCheckPoints()) {
2010-11-21 16:01:39 +03:00
XSetWindowAttributes wa;
wa.override_redirect = True;
2011-01-30 17:34:42 +03:00
::Window window = XCreateWindow(display(), rootWindow(), 0, 0, selfCheckWidth(), selfCheckHeight(),
0, QX11Info::appDepth(), CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa);
XSetWindowBackgroundPixmap(display(), window, pix.handle());
XClearWindow(display(), window);
XMapWindow(display(), window);
2010-11-21 16:01:39 +03:00
// move the window one down to where the result will be rendered too, just in case
// the render would fail completely and eventual check would try to read this window's contents
2011-01-30 17:34:42 +03:00
XMoveWindow(display(), window, p.x() + 1, p.y());
XCompositeRedirectWindow(display(), window, CompositeRedirectAutomatic);
Pixmap wpix = XCompositeNameWindowPixmap(display(), window);
2010-11-21 16:01:39 +03:00
glXWaitX();
Texture texture;
2011-01-30 17:34:42 +03:00
texture.load(wpix, QSize(selfCheckWidth(), selfCheckHeight()), QX11Info::appDepth());
2010-11-21 16:01:39 +03:00
texture.bind();
2011-01-30 17:34:42 +03:00
QRect rect(p.x(), p.y(), selfCheckWidth(), selfCheckHeight());
texture.render(infiniteRegion(), rect);
2010-11-21 16:01:39 +03:00
texture.unbind();
glXWaitGL();
2011-01-30 17:34:42 +03:00
XFreePixmap(display(), wpix);
XDestroyWindow(display(), window);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
err.error(true); // just sync and discard
}
2010-11-21 16:01:39 +03:00
bool SceneOpenGL::selfCheckFinish()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
glXWaitGL();
KXErrorHandler err;
bool ok = true;
2011-01-30 17:34:42 +03:00
foreach (const QPoint & p, selfCheckPoints()) {
QPixmap pix = QPixmap::grabWindow(rootWindow(), p.x(), p.y(), selfCheckWidth(), selfCheckHeight());
2010-11-21 16:01:39 +03:00
QImage img = pix.toImage();
// kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name();
// kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name();
// kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name();
// kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name();
// kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name();
// kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name();
2011-01-30 17:34:42 +03:00
if (img.pixel(0, 0) != QColor(Qt::red).rgb()
|| img.pixel(1, 0) != QColor(Qt::green).rgb()
|| img.pixel(2, 0) != QColor(Qt::blue).rgb()
|| img.pixel(0, 1) != QColor(Qt::white).rgb()
|| img.pixel(1, 1) != QColor(Qt::black).rgb()
|| img.pixel(2, 1) != QColor(Qt::white).rgb()) {
kError(1212) << "OpenGL compositing self-check failed, disabling compositing.";
2010-11-21 16:01:39 +03:00
ok = false;
break;
}
2011-01-30 17:34:42 +03:00
}
if (err.error(true))
2010-11-21 16:01:39 +03:00
ok = false;
2011-01-30 17:34:42 +03:00
if (ok)
kDebug(1212) << "OpenGL compositing self-check passed.";
if (!ok && options->disableCompositingChecks) {
kWarning(1212) << "Compositing checks disabled, proceeding regardless of self-check failure.";
2010-11-21 16:01:39 +03:00
return true;
}
2011-01-30 17:34:42 +03:00
return ok;
}
2010-11-21 16:01:39 +03:00
// the entry function for painting
2011-01-30 17:34:42 +03:00
void SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
2010-11-21 16:01:39 +03:00
QTime t = QTime::currentTime();
2011-01-30 17:34:42 +03:00
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
}
2010-11-21 16:01:39 +03:00
grabXServer();
glXWaitX();
glPushMatrix();
int mask = 0;
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("Paint1");
2010-11-21 16:01:39 +03:00
#endif
2011-01-30 17:34:42 +03:00
paintScreen(&mask, &damage); // call generic implementation
2010-11-21 16:01:39 +03:00
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("Paint2");
2010-11-21 16:01:39 +03:00
#endif
glPopMatrix();
ungrabXServer(); // ungrab before flushBuffer(), it may wait for vsync
2011-01-30 17:34:42 +03:00
if (wspace->overlayWindow()) // show the window only after the first pass, since
2010-11-21 16:01:39 +03:00
wspace->showOverlay(); // that pass may take long
// selfcheck is broken (see Bug 253357)
#if 0
2011-01-30 17:34:42 +03:00
if (!selfCheckDone) {
2010-11-21 16:01:39 +03:00
selfCheckSetup();
damage |= selfCheckRegion();
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
#endif
lastRenderTime = t.elapsed();
2011-01-30 17:34:42 +03:00
flushBuffer(mask, damage);
2010-11-21 16:01:39 +03:00
#if 0
2011-01-30 17:34:42 +03:00
if (!selfCheckDone) {
if (!selfCheckFinish())
QTimer::singleShot(0, Workspace::self(), SLOT(finishCompositing()));
2010-11-21 16:01:39 +03:00
selfCheckDone = true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
#endif
// do cleanup
stacking_order.clear();
2011-01-30 17:34:42 +03:00
checkGLError("PostPaint");
}
2010-11-21 16:01:39 +03:00
// wait for vblank signal before painting
void SceneOpenGL::waitSync()
2011-01-30 17:34:42 +03:00
{
// NOTE that vsync has no effect with indirect rendering
if (waitSyncAvailable()) {
2010-11-21 16:01:39 +03:00
uint sync;
glFlush();
2011-01-30 17:34:42 +03:00
glXGetVideoSync(&sync);
glXWaitVideoSync(2, (sync + 1) % 2, &sync);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
// actually paint to the screen (double-buffer swap or copy from pixmap buffer)
2011-01-30 17:34:42 +03:00
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
{
if (db) {
if (mask & PAINT_SCREEN_REGION) {
2010-11-21 16:01:39 +03:00
waitSync();
2011-01-30 17:34:42 +03:00
if (glXCopySubBuffer) {
foreach (const QRect & r, damage.rects()) {
2010-11-21 16:01:39 +03:00
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
2011-01-30 17:34:42 +03:00
glXCopySubBuffer(display(), glxbuffer, r.x(), y, r.width(), r.height());
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
} else {
// if a shader is bound, copy pixels results in a black screen
// therefore unbind the shader and restore after copying the pixels
GLint shader = 0;
if (ShaderManager::instance()->isShaderBound()) {
glGetIntegerv(GL_CURRENT_PROGRAM, &shader);
glUseProgram(0);
}
2011-01-30 17:34:42 +03:00
// no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
glEnable(GL_SCISSOR_TEST);
glDrawBuffer(GL_FRONT);
2010-11-21 16:01:39 +03:00
int xpos = 0;
int ypos = 0;
2011-01-30 17:34:42 +03:00
foreach (const QRect & r, damage.rects()) {
2010-11-21 16:01:39 +03:00
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
// Move raster position relatively using glBitmap() rather
// than using glRasterPos2f() - the latter causes drawing
// artefacts at the bottom screen edge with some gfx cards
// glRasterPos2f( r.x(), r.y() + r.height());
2011-01-30 17:34:42 +03:00
glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL);
2010-11-21 16:01:39 +03:00
xpos = r.x();
ypos = y;
2011-01-30 17:34:42 +03:00
glScissor(r.x(), y, r.width(), r.height());
glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
glDrawBuffer(GL_BACK);
glDisable(GL_SCISSOR_TEST);
// rebind previously bound shader
if (ShaderManager::instance()->isShaderBound()) {
glUseProgram(shader);
}
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
} else {
2010-11-21 16:01:39 +03:00
waitSync();
2011-01-30 17:34:42 +03:00
glXSwapBuffers(display(), glxbuffer);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
glXWaitGL();
XFlush(display());
} else {
2010-11-21 16:01:39 +03:00
glFlush();
glXWaitGL();
waitSync();
2011-01-30 17:34:42 +03:00
if (mask & PAINT_SCREEN_REGION)
foreach (const QRect & r, damage.rects())
XCopyArea(display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
2010-11-21 16:01:39 +03:00
else
2011-01-30 17:34:42 +03:00
XCopyArea(display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0);
XFlush(display());
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
//****************************************
// SceneOpenGL::Texture
//****************************************
void SceneOpenGL::Texture::init()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
glxpixmap = None;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
void SceneOpenGL::Texture::release()
2011-01-30 17:34:42 +03:00
{
if (glxpixmap != None) {
if (!options->glStrictBinding) {
glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT);
}
2011-01-30 17:34:42 +03:00
glXDestroyPixmap(display(), glxpixmap);
2010-11-21 16:01:39 +03:00
glxpixmap = None;
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
void SceneOpenGL::Texture::findTarget()
2011-01-30 17:34:42 +03:00
{
2010-11-21 16:01:39 +03:00
unsigned int new_target = 0;
2011-01-30 17:34:42 +03:00
if (glXQueryDrawable && glxpixmap != None)
glXQueryDrawable(display(), glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
// HACK: this used to be a hack for Xgl.
// without this hack the NVIDIA blob aborts when trying to bind a texture from
// a pixmap icon
if (new_target == 0) {
if (NPOTTextureSupported() ||
(isPowerOfTwo(mSize.width()) && isPowerOfTwo(mSize.height()))) {
new_target = GLX_TEXTURE_2D_EXT;
} else {
new_target = GLX_TEXTURE_RECTANGLE_EXT;
}
}
2011-01-30 17:34:42 +03:00
switch(new_target) {
case GLX_TEXTURE_2D_EXT:
mTarget = GL_TEXTURE_2D;
mScale.setWidth(1.0f / mSize.width());
mScale.setHeight(1.0f / mSize.height());
break;
case GLX_TEXTURE_RECTANGLE_EXT:
mTarget = GL_TEXTURE_RECTANGLE_ARB;
mScale.setWidth(1.0f);
mScale.setHeight(1.0f);
break;
default:
abort();
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
2011-01-30 17:34:42 +03:00
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
int depth, QRegion region)
{
2010-11-21 16:01:39 +03:00
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("TextureLoad1");
2010-11-21 16:01:39 +03:00
#endif
2011-01-30 17:34:42 +03:00
if (pix == None || size.isEmpty() || depth < 1)
2010-11-21 16:01:39 +03:00
return false;
2011-01-30 17:34:42 +03:00
if (fbcdrawableinfo[ depth ].fbconfig == NULL) {
kDebug(1212) << "No framebuffer configuration for depth " << depth
<< "; not binding pixmap" << endl;
return false;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
mSize = size;
2011-01-30 17:34:42 +03:00
if (mTexture == None || !region.isEmpty()) {
// new texture, or texture contents changed; mipmaps now invalid
2010-11-21 16:01:39 +03:00
setDirty();
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("TextureLoad2");
2010-11-21 16:01:39 +03:00
#endif
// tfp mode, simply bind the pixmap to texture
2011-01-30 17:34:42 +03:00
if (mTexture == None)
createTexture();
// The GLX pixmap references the contents of the original pixmap, so it doesn't
// need to be recreated when the contents change.
// The texture may or may not use the same storage depending on the EXT_tfp
// implementation. When options->glStrictBinding is true, the texture uses
// a different storage and needs to be updated with a call to
// glXBindTexImageEXT() when the contents of the pixmap has changed.
2011-01-30 17:34:42 +03:00
if (glxpixmap != None)
glBindTexture(mTarget, mTexture);
else {
int attrs[] = {
GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap,
None, None, None
2011-01-30 17:34:42 +03:00
};
// Specifying the texture target explicitly is reported to cause a performance
// regression with R300G (see bug #256654).
2011-01-30 17:34:42 +03:00
if (GLPlatform::instance()->driver() != Driver_R300G) {
if ((fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
(GLTexture::NPOTTextureSupported() ||
(isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
2011-01-30 17:34:42 +03:00
} else if (fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
}
glxpixmap = glXCreatePixmap(display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
2010-11-21 16:01:39 +03:00
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("TextureLoadTFP1");
2010-11-21 16:01:39 +03:00
#endif
findTarget();
y_inverted = fbcdrawableinfo[ depth ].y_inverted ? true : false;
can_use_mipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false;
2011-01-30 17:34:42 +03:00
glBindTexture(mTarget, mTexture);
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("TextureLoadTFP2");
#endif
2011-01-30 17:34:42 +03:00
if (!options->glStrictBinding)
glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
}
if (options->glStrictBinding)
// Mark the texture as damaged so it will be updated on the next call to bind()
2011-01-30 17:34:42 +03:00
glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
2010-11-21 16:01:39 +03:00
#ifdef CHECK_GL_ERROR
2011-01-30 17:34:42 +03:00
checkGLError("TextureLoad0");
2010-11-21 16:01:39 +03:00
#endif
return true;
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
void SceneOpenGL::Texture::bind()
2011-01-30 17:34:42 +03:00
{
glEnable(mTarget);
glBindTexture(mTarget, mTexture);
if (options->glStrictBinding) {
assert(glxpixmap != None);
glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT);
glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
2010-11-21 16:01:39 +03:00
setDirty(); // Mipmaps have to be regenerated after updating the texture
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
enableFilter();
2011-01-30 17:34:42 +03:00
if (hasGLVersion(1, 4, 0)) {
2010-11-21 16:01:39 +03:00
// Lod bias makes the trilinear-filtered texture look a bit sharper
2011-01-30 17:34:42 +03:00
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f);
2010-11-21 16:01:39 +03:00
}
2011-01-30 17:34:42 +03:00
}
2010-11-21 16:01:39 +03:00
void SceneOpenGL::Texture::unbind()
2011-01-30 17:34:42 +03:00
{
if (hasGLVersion(1, 4, 0)) {
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f);
}
if (options->glStrictBinding) {
assert(glxpixmap != None);
glBindTexture(mTarget, mTexture);
glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT);
}
2010-11-21 16:01:39 +03:00
GLTexture::unbind();
2011-01-30 17:34:42 +03:00
}