2010-11-21 13:01:39 +00:00
|
|
|
/********************************************************************
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
|
2012-08-26 15:14:23 +00:00
|
|
|
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
|
2010-11-21 13:01:39 +00:00
|
|
|
|
|
|
|
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/>.
|
|
|
|
*********************************************************************/
|
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
// TODO: cmake magic
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
|
|
// own
|
|
|
|
#include "glxbackend.h"
|
|
|
|
// kwin
|
|
|
|
#include "options.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "overlaywindow.h"
|
|
|
|
// kwin libs
|
|
|
|
#include <kwinglplatform.h>
|
|
|
|
// KDE
|
|
|
|
#include <KDE/KDebug>
|
|
|
|
#include <KDE/KXErrorHandler>
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
GlxBackend::GlxBackend()
|
|
|
|
: OpenGLBackend()
|
2013-03-11 15:27:24 +00:00
|
|
|
, window(None)
|
2013-03-11 15:05:47 +00:00
|
|
|
, fbconfig(NULL)
|
2013-03-11 15:27:24 +00:00
|
|
|
, glxWindow(None)
|
2013-03-11 15:05:47 +00:00
|
|
|
, ctx(None)
|
2012-11-13 21:19:01 +00:00
|
|
|
, haveSwapInterval(false)
|
2012-08-26 15:14:23 +00:00
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
GlxBackend::~GlxBackend()
|
|
|
|
{
|
|
|
|
// TODO: cleanup in error case
|
|
|
|
// do cleanup after initBuffer()
|
|
|
|
cleanupGL();
|
|
|
|
glXMakeCurrent(display(), None, NULL);
|
2013-03-11 15:27:24 +00:00
|
|
|
|
2013-03-11 15:05:47 +00:00
|
|
|
if (ctx)
|
|
|
|
glXDestroyContext(display(), ctx);
|
2013-03-11 15:27:24 +00:00
|
|
|
|
|
|
|
if (glxWindow)
|
|
|
|
glXDestroyWindow(display(), glxWindow);
|
|
|
|
|
|
|
|
if (window)
|
|
|
|
XDestroyWindow(display(), window);
|
|
|
|
|
|
|
|
overlayWindow()->destroy();
|
2012-08-26 15:14:23 +00:00
|
|
|
checkGLError("Cleanup");
|
|
|
|
}
|
|
|
|
|
|
|
|
void GlxBackend::init()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2010-11-21 13:01:39 +00:00
|
|
|
initGLX();
|
2012-10-03 08:28:43 +00:00
|
|
|
// require at least GLX 1.3
|
|
|
|
if (!hasGLXVersion(1, 3)) {
|
|
|
|
setFailed("Requires at least GLX 1.3");
|
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
if (!initDrawableConfigs()) {
|
|
|
|
setFailed("Could not initialize the drawable configs");
|
2010-11-21 13:01:39 +00:00
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
if (!initBuffer()) {
|
|
|
|
setFailed("Could not initialize the buffer");
|
|
|
|
return;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
if (!initRenderingContext()) {
|
|
|
|
setFailed("Could not initialize rendering context");
|
2011-04-28 20:06:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
// Initialize OpenGL
|
|
|
|
GLPlatform *glPlatform = GLPlatform::instance();
|
2012-09-29 11:19:35 +00:00
|
|
|
glPlatform->detect(GlxPlatformInterface);
|
2012-08-26 15:14:23 +00:00
|
|
|
glPlatform->printResults();
|
2012-09-29 11:19:35 +00:00
|
|
|
initGL(GlxPlatformInterface);
|
2010-11-21 13:01:39 +00:00
|
|
|
// Check whether certain features are supported
|
2012-11-13 21:19:01 +00:00
|
|
|
haveSwapInterval = glXSwapIntervalMESA || glXSwapIntervalEXT || glXSwapIntervalSGI;
|
2012-03-29 20:11:28 +00:00
|
|
|
if (options->isGlVSync()) {
|
2013-03-11 15:05:47 +00:00
|
|
|
if (glXGetVideoSync && haveSwapInterval && glXIsDirect(display(), ctx)) {
|
2012-03-29 20:11:28 +00:00
|
|
|
unsigned int sync;
|
|
|
|
if (glXGetVideoSync(&sync) == 0) {
|
|
|
|
if (glXWaitVideoSync(1, 0, &sync) == 0) {
|
|
|
|
// NOTICE at this time we should actually check whether we can successfully
|
|
|
|
// deactivate the swapInterval "glXSwapInterval(0) == 0"
|
|
|
|
// (because we don't actually want it active unless we explicitly run a glXSwapBuffers)
|
|
|
|
// However mesa/dri will return a range error (6) because deactivating the
|
|
|
|
// swapinterval (as of today) seems completely unsupported
|
2012-08-26 15:14:23 +00:00
|
|
|
setHasWaitSync(true);
|
2013-02-18 22:17:46 +00:00
|
|
|
setSwapInterval(1);
|
2012-03-29 20:11:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
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);
|
2011-01-30 14:34:42 +00:00
|
|
|
} else
|
2012-11-13 21:19:01 +00:00
|
|
|
qWarning() << "NO VSYNC! glXGetVideoSync, haveSwapInterval, glXIsDirect" <<
|
2013-03-11 15:05:47 +00:00
|
|
|
bool(glXGetVideoSync) << haveSwapInterval << glXIsDirect(display(), ctx);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2012-10-13 08:33:38 +00:00
|
|
|
if (glPlatform->isVirtualBox()) {
|
|
|
|
// VirtualBox does not support glxQueryDrawable
|
|
|
|
// this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
|
|
|
|
// and the GLPlatform has not been initialized at the moment when initGLX() is called.
|
|
|
|
glXQueryDrawable = NULL;
|
|
|
|
}
|
2013-03-11 15:27:24 +00:00
|
|
|
|
2013-03-11 15:05:47 +00:00
|
|
|
setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
|
2013-03-11 15:27:24 +00:00
|
|
|
|
|
|
|
kDebug(1212) << "Direct rendering:" << isDirectRendering() << endl;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
bool GlxBackend::initRenderingContext()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2013-03-11 15:19:45 +00:00
|
|
|
bool direct = options->isGlDirect();
|
|
|
|
|
|
|
|
ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, NULL, direct);
|
|
|
|
|
|
|
|
if (!ctx) {
|
|
|
|
kDebug(1212) << "Failed to create an OpenGL context.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-11 15:27:24 +00:00
|
|
|
if (!glXMakeCurrent(display(), glxWindow, ctx)) {
|
2013-03-11 15:19:45 +00:00
|
|
|
kDebug(1212) << "Failed to make the OpenGL context current.";
|
|
|
|
glXDestroyContext(display(), ctx);
|
|
|
|
ctx = 0;
|
|
|
|
return false;
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
2013-03-11 15:19:45 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
bool GlxBackend::initBuffer()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2013-03-11 15:16:05 +00:00
|
|
|
if (!initFbConfig())
|
2010-11-21 13:01:39 +00:00
|
|
|
return false;
|
2013-03-11 15:16:05 +00:00
|
|
|
|
2013-03-11 15:27:24 +00:00
|
|
|
if (overlayWindow()->create()) {
|
|
|
|
// Try to create double-buffered window in the overlay
|
2013-03-11 15:05:47 +00:00
|
|
|
XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbconfig);
|
2010-11-21 13:01:39 +00:00
|
|
|
XSetWindowAttributes attrs;
|
2011-01-30 14:34:42 +00:00
|
|
|
attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone);
|
2013-03-11 15:27:24 +00:00
|
|
|
window = XCreateWindow(display(), overlayWindow()->window(), 0, 0, displayWidth(), displayHeight(),
|
|
|
|
0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs);
|
|
|
|
glxWindow = glXCreateWindow(display(), fbconfig, window, NULL);
|
|
|
|
overlayWindow()->setup(window);
|
2011-01-30 14:34:42 +00:00
|
|
|
XFree(visual);
|
|
|
|
} else {
|
2013-03-11 15:27:24 +00:00
|
|
|
kError(1212) << "Failed to create overlay window";
|
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2013-03-11 15:27:24 +00:00
|
|
|
|
2010-11-21 13:01:39 +00:00
|
|
|
int vis_buffer;
|
2013-03-11 15:05:47 +00:00
|
|
|
glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, &vis_buffer);
|
|
|
|
XVisualInfo* visinfo_buffer = glXGetVisualFromFBConfig(display(), fbconfig);
|
2011-01-30 14:34:42 +00:00
|
|
|
kDebug(1212) << "Buffer visual (depth " << visinfo_buffer->depth << "): 0x" << QString::number(vis_buffer, 16);
|
|
|
|
XFree(visinfo_buffer);
|
2013-03-11 15:27:24 +00:00
|
|
|
|
2010-11-21 13:01:39 +00:00
|
|
|
return true;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2013-03-11 15:16:05 +00:00
|
|
|
bool GlxBackend::initFbConfig()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2013-03-11 15:27:24 +00:00
|
|
|
const int attribs[] = {
|
2013-03-21 11:40:49 +00:00
|
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
2013-03-11 15:16:05 +00:00
|
|
|
GLX_RED_SIZE, 1,
|
|
|
|
GLX_GREEN_SIZE, 1,
|
|
|
|
GLX_BLUE_SIZE, 1,
|
|
|
|
GLX_ALPHA_SIZE, 0,
|
|
|
|
GLX_DEPTH_SIZE, 0,
|
|
|
|
GLX_STENCIL_SIZE, 0,
|
|
|
|
GLX_CONFIG_CAVEAT, GLX_NONE,
|
|
|
|
GLX_DOUBLEBUFFER, true,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
// Try to find a double buffered configuration
|
|
|
|
int count = 0;
|
|
|
|
GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
|
2012-10-30 17:20:00 +00:00
|
|
|
|
2013-03-11 15:16:05 +00:00
|
|
|
if (count > 0) {
|
2013-03-11 15:27:24 +00:00
|
|
|
fbconfig = configs[0];
|
2013-03-11 15:16:05 +00:00
|
|
|
XFree(configs);
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2013-03-11 15:16:05 +00:00
|
|
|
|
2013-03-11 15:27:24 +00:00
|
|
|
if (fbconfig == NULL) {
|
2013-03-11 15:16:05 +00:00
|
|
|
kError(1212) << "Failed to find a usable framebuffer configuration";
|
2010-11-21 13:01:39 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2013-03-11 15:16:05 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
bool GlxBackend::initDrawableConfigs()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2013-03-12 16:13:40 +00:00
|
|
|
const int attribs[] = {
|
|
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
|
|
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
|
|
|
|
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
|
|
|
GLX_X_RENDERABLE, True,
|
|
|
|
GLX_CONFIG_CAVEAT, GLX_NONE,
|
|
|
|
GLX_RED_SIZE, 5,
|
|
|
|
GLX_GREEN_SIZE, 5,
|
|
|
|
GLX_BLUE_SIZE, 5,
|
|
|
|
GLX_ALPHA_SIZE, 0,
|
|
|
|
GLX_STENCIL_SIZE, 0,
|
|
|
|
GLX_DEPTH_SIZE, 0,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
|
|
|
|
|
|
|
|
if (count < 1) {
|
|
|
|
kError(1212) << "Could not find any usable framebuffer configurations.";
|
|
|
|
return false;
|
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
for (int i = 0; i <= 32; i++) {
|
2013-03-12 16:13:40 +00:00
|
|
|
fbcdrawableinfo[i].fbconfig = NULL;
|
|
|
|
fbcdrawableinfo[i].bind_texture_format = 0;
|
|
|
|
fbcdrawableinfo[i].texture_targets = 0;
|
|
|
|
fbcdrawableinfo[i].y_inverted = 0;
|
|
|
|
fbcdrawableinfo[i].mipmap = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the first usable framebuffer configuration for each depth.
|
|
|
|
// Single-buffered ones will appear first in the list.
|
|
|
|
const int depths[] = { 15, 16, 24, 30, 32 };
|
|
|
|
for (unsigned int i = 0; i < sizeof(depths) / sizeof(depths[0]); i++) {
|
|
|
|
const int depth = depths[i];
|
|
|
|
|
|
|
|
for (int j = 0; j < count; j++) {
|
|
|
|
int alpha_size, buffer_size;
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_ALPHA_SIZE, &alpha_size);
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BUFFER_SIZE, &buffer_size);
|
|
|
|
|
|
|
|
if (buffer_size != depth && (buffer_size - alpha_size) != depth)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
XVisualInfo *vi = glXGetVisualFromFBConfig(display(), configs[j]);
|
2011-01-30 14:34:42 +00:00
|
|
|
if (vi == NULL)
|
2010-11-21 13:01:39 +00:00
|
|
|
continue;
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
int visual_depth = vi->depth;
|
2011-01-30 14:34:42 +00:00
|
|
|
XFree(vi);
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
if (visual_depth != depth)
|
2010-11-21 13:01:39 +00:00
|
|
|
continue;
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
int bind_rgb, bind_rgba;
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
|
|
|
|
|
|
|
|
// Skip this config if it cannot be bound to a texture
|
|
|
|
if (!bind_rgb && !bind_rgba)
|
2010-11-21 13:01:39 +00:00
|
|
|
continue;
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
int texture_format;
|
|
|
|
if (depth == 32)
|
|
|
|
texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
|
|
|
|
else
|
|
|
|
texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
|
|
|
|
|
|
|
|
int y_inverted, texture_targets;
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
|
|
|
|
glXGetFBConfigAttrib(display(), configs[j], GLX_Y_INVERTED_EXT, &y_inverted);
|
|
|
|
|
|
|
|
fbcdrawableinfo[depth].fbconfig = configs[j];
|
|
|
|
fbcdrawableinfo[depth].bind_texture_format = texture_format;
|
|
|
|
fbcdrawableinfo[depth].texture_targets = texture_targets;
|
|
|
|
fbcdrawableinfo[depth].y_inverted = y_inverted;
|
|
|
|
fbcdrawableinfo[depth].mipmap = 0;
|
|
|
|
break;
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
if (count)
|
|
|
|
XFree(configs);
|
|
|
|
|
|
|
|
if (fbcdrawableinfo[DefaultDepth(display(), DefaultScreen(display()))].fbconfig == NULL) {
|
|
|
|
kError(1212) << "Could not find a framebuffer configuration for the default depth.";
|
2010-11-21 13:01:39 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2013-03-12 16:13:40 +00:00
|
|
|
|
|
|
|
if (fbcdrawableinfo[32].fbconfig == NULL) {
|
|
|
|
kError(1212) << "Could not find a framebuffer configuration for depth 32.";
|
2010-11-21 13:01:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-03-11 15:10:14 +00:00
|
|
|
|
|
|
|
for (int i = 0; i <= 32; i++) {
|
|
|
|
if (fbcdrawableinfo[i].fbconfig == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int vis_drawable = 0;
|
|
|
|
glXGetFBConfigAttrib(display(), fbcdrawableinfo[i].fbconfig, GLX_VISUAL_ID, &vis_drawable);
|
|
|
|
|
|
|
|
kDebug(1212) << "Drawable visual (depth " << i << "): 0x" << QString::number(vis_drawable, 16);
|
|
|
|
}
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-11-13 21:19:01 +00:00
|
|
|
void GlxBackend::setSwapInterval(int interval)
|
|
|
|
{
|
|
|
|
if (glXSwapIntervalEXT)
|
2013-03-11 15:27:24 +00:00
|
|
|
glXSwapIntervalEXT(display(), glxWindow, interval);
|
2012-11-13 21:19:01 +00:00
|
|
|
else if (glXSwapIntervalMESA)
|
|
|
|
glXSwapIntervalMESA(interval);
|
|
|
|
else if (glXSwapIntervalSGI)
|
|
|
|
glXSwapIntervalSGI(interval);
|
|
|
|
}
|
|
|
|
|
2012-03-29 20:11:28 +00:00
|
|
|
#define VSYNC_DEBUG 0
|
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
void GlxBackend::waitSync()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
|
|
|
// NOTE that vsync has no effect with indirect rendering
|
|
|
|
if (waitSyncAvailable()) {
|
2012-03-29 20:11:28 +00:00
|
|
|
#if VSYNC_DEBUG
|
2012-08-26 15:14:23 +00:00
|
|
|
startRenderTimer();
|
2012-03-29 20:11:28 +00:00
|
|
|
#endif
|
2010-11-21 13:01:39 +00:00
|
|
|
uint sync;
|
2012-03-29 20:11:28 +00:00
|
|
|
#if 0
|
|
|
|
// TODO: why precisely is this important?
|
|
|
|
// the sync counter /can/ perform multiple steps during glXGetVideoSync & glXWaitVideoSync
|
|
|
|
// but this only leads to waiting for two frames??!?
|
2011-01-30 14:34:42 +00:00
|
|
|
glXGetVideoSync(&sync);
|
|
|
|
glXWaitVideoSync(2, (sync + 1) % 2, &sync);
|
2012-07-17 23:50:43 +00:00
|
|
|
#else
|
2012-03-29 20:11:28 +00:00
|
|
|
glXWaitVideoSync(1, 0, &sync);
|
2012-07-17 23:50:43 +00:00
|
|
|
#endif
|
2012-03-29 20:11:28 +00:00
|
|
|
#if VSYNC_DEBUG
|
2012-07-17 23:50:43 +00:00
|
|
|
static int waitTime = 0, waitCounter = 0, doubleSyncCounter = 0;
|
2012-08-26 15:14:23 +00:00
|
|
|
if (renderTime() > 11)
|
2012-07-17 23:50:43 +00:00
|
|
|
++doubleSyncCounter;
|
2012-08-26 15:14:23 +00:00
|
|
|
waitTime += renderTime();
|
2012-03-29 20:11:28 +00:00
|
|
|
++waitCounter;
|
|
|
|
if (waitCounter > 99)
|
|
|
|
{
|
2012-07-17 23:50:43 +00:00
|
|
|
qDebug() << "mean vsync wait time:" << float((float)waitTime / (float)waitCounter) << doubleSyncCounter << "/100";
|
|
|
|
doubleSyncCounter = waitTime = waitCounter = 0;
|
2012-03-29 20:11:28 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
startRenderTimer(); // yes, the framerate shall be constant anyway.
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-03-29 20:11:28 +00:00
|
|
|
#undef VSYNC_DEBUG
|
|
|
|
|
2012-10-07 11:43:31 +00:00
|
|
|
void GlxBackend::present()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2013-03-11 15:27:24 +00:00
|
|
|
const QRegion displayRegion(0, 0, displayWidth(), displayHeight());
|
|
|
|
const bool fullRepaint = (lastDamage() == displayRegion);
|
|
|
|
|
|
|
|
if (fullRepaint) {
|
|
|
|
if (haveSwapInterval) {
|
|
|
|
glXSwapBuffers(display(), glxWindow);
|
|
|
|
startRenderTimer();
|
|
|
|
} else {
|
|
|
|
waitSync(); // calls startRenderTimer();
|
|
|
|
glXSwapBuffers(display(), glxWindow);
|
|
|
|
}
|
|
|
|
} else if (glXCopySubBuffer) {
|
|
|
|
waitSync();
|
|
|
|
foreach (const QRect & r, lastDamage().rects()) {
|
|
|
|
// convert to OpenGL coordinates
|
|
|
|
int y = displayHeight() - r.y() - r.height();
|
|
|
|
glXCopySubBuffer(display(), glxWindow, r.x(), y, r.width(), r.height());
|
|
|
|
}
|
|
|
|
} else { // Copy Pixels
|
|
|
|
// if a shader is bound or the texture unit is enabled, 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);
|
|
|
|
}
|
|
|
|
bool reenableTexUnit = false;
|
|
|
|
if (glIsEnabled(GL_TEXTURE_2D)) {
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
reenableTexUnit = true;
|
|
|
|
}
|
|
|
|
// no idea why glScissor() is used, but Compiz has it and it doesn't seem to hurt
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
glDrawBuffer(GL_FRONT);
|
|
|
|
waitSync();
|
|
|
|
int xpos = 0;
|
|
|
|
int ypos = 0;
|
|
|
|
foreach (const QRect & r, lastDamage().rects()) {
|
|
|
|
// 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());
|
|
|
|
glBitmap(0, 0, 0, 0, r.x() - xpos, y - ypos, NULL);
|
|
|
|
xpos = r.x();
|
|
|
|
ypos = y;
|
|
|
|
glScissor(r.x(), y, r.width(), r.height());
|
|
|
|
glCopyPixels(r.x(), y, r.width(), r.height(), GL_COLOR);
|
|
|
|
}
|
|
|
|
glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
|
|
|
|
glDrawBuffer(GL_BACK);
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
if (reenableTexUnit) {
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
}
|
|
|
|
// rebind previously bound shader
|
|
|
|
if (ShaderManager::instance()->isShaderBound()) {
|
|
|
|
glUseProgram(shader);
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-11 15:27:24 +00:00
|
|
|
|
|
|
|
glXWaitGL();
|
2013-02-18 22:17:46 +00:00
|
|
|
setLastDamage(QRegion());
|
2012-03-29 20:11:28 +00:00
|
|
|
XFlush(display());
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
void GlxBackend::screenGeometryChanged(const QSize &size)
|
2011-11-26 15:15:46 +00:00
|
|
|
{
|
2013-03-11 15:27:24 +00:00
|
|
|
glXMakeCurrent(display(), None, NULL);
|
|
|
|
|
|
|
|
XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
|
|
|
|
overlayWindow()->setup(window);
|
|
|
|
XSync(display(), false);
|
|
|
|
|
|
|
|
glXMakeCurrent(display(), glxWindow, ctx);
|
|
|
|
glViewport(0, 0, size.width(), size.height());
|
2011-11-26 15:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture)
|
|
|
|
{
|
|
|
|
return new GlxTexture(texture, this);
|
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
void GlxBackend::prepareRenderingFrame()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2012-08-26 15:14:23 +00:00
|
|
|
if (!lastDamage().isEmpty())
|
2012-10-07 11:43:31 +00:00
|
|
|
present();
|
2012-08-26 15:14:23 +00:00
|
|
|
glXWaitX();
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2013-03-10 21:18:30 +00:00
|
|
|
void GlxBackend::endRenderingFrame(const QRegion &damage)
|
2012-08-26 15:14:23 +00:00
|
|
|
{
|
|
|
|
setLastDamage(damage);
|
|
|
|
glFlush();
|
|
|
|
|
|
|
|
if (overlayWindow()->window()) // show the window only after the first pass,
|
|
|
|
overlayWindow()->show(); // since that pass may take long
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************************
|
|
|
|
* GlxTexture
|
|
|
|
*******************************************************/
|
|
|
|
GlxTexture::GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend)
|
|
|
|
: SceneOpenGL::TexturePrivate()
|
|
|
|
, q(texture)
|
|
|
|
, m_backend(backend)
|
|
|
|
, m_glxpixmap(None)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
GlxTexture::~GlxTexture()
|
2011-07-18 15:55:39 +00:00
|
|
|
{
|
|
|
|
if (m_glxpixmap != None) {
|
2012-02-20 09:25:13 +00:00
|
|
|
if (!options->isGlStrictBinding()) {
|
2011-07-18 15:55:39 +00:00
|
|
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
2011-02-05 10:55:10 +00:00
|
|
|
}
|
2011-07-18 15:55:39 +00:00
|
|
|
glXDestroyPixmap(display(), m_glxpixmap);
|
2011-08-25 08:29:23 +00:00
|
|
|
m_glxpixmap = None;
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
void GlxTexture::onDamage()
|
|
|
|
{
|
|
|
|
if (options->isGlStrictBinding() && m_glxpixmap) {
|
|
|
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
|
|
|
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
|
|
|
}
|
|
|
|
GLTexturePrivate::onDamage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GlxTexture::findTarget()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2010-11-21 13:01:39 +00:00
|
|
|
unsigned int new_target = 0;
|
2012-08-26 15:14:23 +00:00
|
|
|
if (glXQueryDrawable && m_glxpixmap != None)
|
|
|
|
glXQueryDrawable(display(), m_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
|
2011-02-05 10:56:48 +00:00
|
|
|
// 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) {
|
2012-08-26 15:14:23 +00:00
|
|
|
if (GLTexture::NPOTTextureSupported() ||
|
|
|
|
(isPowerOfTwo(m_size.width()) && isPowerOfTwo(m_size.height()))) {
|
2011-02-05 10:56:48 +00:00
|
|
|
new_target = GLX_TEXTURE_2D_EXT;
|
|
|
|
} else {
|
|
|
|
new_target = GLX_TEXTURE_RECTANGLE_EXT;
|
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
switch(new_target) {
|
|
|
|
case GLX_TEXTURE_2D_EXT:
|
2012-08-26 15:14:23 +00:00
|
|
|
m_target = GL_TEXTURE_2D;
|
|
|
|
m_scale.setWidth(1.0f / m_size.width());
|
|
|
|
m_scale.setHeight(1.0f / m_size.height());
|
2011-01-30 14:34:42 +00:00
|
|
|
break;
|
|
|
|
case GLX_TEXTURE_RECTANGLE_EXT:
|
2012-08-26 15:14:23 +00:00
|
|
|
m_target = GL_TEXTURE_RECTANGLE_ARB;
|
|
|
|
m_scale.setWidth(1.0f);
|
|
|
|
m_scale.setHeight(1.0f);
|
2011-01-30 14:34:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
2010-11-21 13:01:39 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
bool GlxTexture::loadTexture(const Pixmap& pix, const QSize& size, int depth)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2010-11-21 13:01:39 +00:00
|
|
|
#ifdef CHECK_GL_ERROR
|
2011-01-30 14:34:42 +00:00
|
|
|
checkGLError("TextureLoad1");
|
2010-11-21 13:01:39 +00:00
|
|
|
#endif
|
2011-01-30 14:34:42 +00:00
|
|
|
if (pix == None || size.isEmpty() || depth < 1)
|
2010-11-21 13:01:39 +00:00
|
|
|
return false;
|
2012-08-26 15:14:23 +00:00
|
|
|
if (m_backend->fbcdrawableinfo[ depth ].fbconfig == NULL) {
|
2011-01-30 14:34:42 +00:00
|
|
|
kDebug(1212) << "No framebuffer configuration for depth " << depth
|
|
|
|
<< "; not binding pixmap" << endl;
|
2011-01-30 10:55:27 +00:00
|
|
|
return false;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
m_size = size;
|
2011-07-18 15:55:39 +00:00
|
|
|
// new texture, or texture contents changed; mipmaps now invalid
|
2012-08-26 15:14:23 +00:00
|
|
|
q->setDirty();
|
2010-11-21 13:01:39 +00:00
|
|
|
|
|
|
|
#ifdef CHECK_GL_ERROR
|
2011-01-30 14:34:42 +00:00
|
|
|
checkGLError("TextureLoad2");
|
2010-11-21 13:01:39 +00:00
|
|
|
#endif
|
2011-01-30 10:55:27 +00:00
|
|
|
// tfp mode, simply bind the pixmap to texture
|
2012-08-26 15:14:23 +00:00
|
|
|
glGenTextures(1, &m_texture);
|
2011-01-30 10:55:27 +00:00
|
|
|
// 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-07-18 15:55:39 +00:00
|
|
|
int attrs[] = {
|
2012-08-26 15:14:23 +00:00
|
|
|
GLX_TEXTURE_FORMAT_EXT, m_backend->fbcdrawableinfo[ depth ].bind_texture_format,
|
|
|
|
GLX_MIPMAP_TEXTURE_EXT, m_backend->fbcdrawableinfo[ depth ].mipmap > 0,
|
2011-07-18 15:55:39 +00:00
|
|
|
None, None, None
|
|
|
|
};
|
|
|
|
// Specifying the texture target explicitly is reported to cause a performance
|
|
|
|
// regression with R300G (see bug #256654).
|
|
|
|
if (GLPlatform::instance()->driver() != Driver_R300G) {
|
2012-08-26 15:14:23 +00:00
|
|
|
if ((m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
|
2011-07-18 15:55:39 +00:00
|
|
|
(GLTexture::NPOTTextureSupported() ||
|
|
|
|
(isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
|
|
|
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
|
|
|
attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
|
2012-08-26 15:14:23 +00:00
|
|
|
} else if (m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
|
2011-07-18 15:55:39 +00:00
|
|
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
|
|
|
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2011-07-18 15:55:39 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
m_glxpixmap = glXCreatePixmap(display(), m_backend->fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
|
2010-11-21 13:01:39 +00:00
|
|
|
#ifdef CHECK_GL_ERROR
|
2011-07-18 15:55:39 +00:00
|
|
|
checkGLError("TextureLoadTFP1");
|
2010-11-21 13:01:39 +00:00
|
|
|
#endif
|
2011-07-18 15:55:39 +00:00
|
|
|
findTarget();
|
2012-08-26 15:14:23 +00:00
|
|
|
m_yInverted = m_backend->fbcdrawableinfo[ depth ].y_inverted ? true : false;
|
|
|
|
m_canUseMipmaps = m_backend->fbcdrawableinfo[ depth ].mipmap > 0;
|
|
|
|
q->setFilter(m_backend->fbcdrawableinfo[ depth ].mipmap > 0 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST);
|
|
|
|
glBindTexture(m_target, m_texture);
|
2011-01-30 10:55:27 +00:00
|
|
|
#ifdef CHECK_GL_ERROR
|
2011-07-18 15:55:39 +00:00
|
|
|
checkGLError("TextureLoadTFP2");
|
2011-01-30 10:55:27 +00:00
|
|
|
#endif
|
2012-08-26 15:14:23 +00:00
|
|
|
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
2010-11-21 13:01:39 +00:00
|
|
|
#ifdef CHECK_GL_ERROR
|
2011-01-30 14:34:42 +00:00
|
|
|
checkGLError("TextureLoad0");
|
2010-11-21 13:01:39 +00:00
|
|
|
#endif
|
2012-01-07 11:12:29 +00:00
|
|
|
unbind();
|
2010-11-21 13:01:39 +00:00
|
|
|
return true;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2010-11-21 13:01:39 +00:00
|
|
|
|
2012-08-26 15:14:23 +00:00
|
|
|
OpenGLBackend *GlxTexture::backend()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2012-08-26 15:14:23 +00:00
|
|
|
return m_backend;
|
2012-01-07 11:12:29 +00:00
|
|
|
}
|
2012-08-26 15:14:23 +00:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
#endif
|