Split out the windowing system related part of SceneOpenGL

The handling for creating and managing the OpenGL context is
split out of the SceneOpenGL into the abstract OpenGLBackend
and it's two subclasses GlxBackend and EglOnXBackend.

The backends take care of creating the OpenGL context on the
windowing system, e.g. on glx an OpenGL context on the overlay
window is created and in the egl case an EGL context is created.
This means that the SceneOpenGL itself does not have to care
about the specific underlying infrastructure.

Furthermore the backend provides the Textures for the specific
texture from pixmap operations. For that in each of the backend
files an additional subclass of the TexturePrivate is defined.
These subclasses hold the EglImage and GLXPixmap respectively.

The backend is able to create such a private texture and for
that the ctor of the Texture is changed to take the backend as
a parameter and the Scene provides a factory method for
creating Textures. To make this work inside Window the Textures
are now hold as pointers which seems a better choice anyway as
to the member functions pointers are passed.
icc-effect-5.14.5
Martin Gräßlin 2012-08-26 17:14:23 +02:00
parent 66f568342a
commit 6152cc4fa5
12 changed files with 1073 additions and 594 deletions

View File

@ -109,6 +109,8 @@ set(kwin_KDEINIT_SRCS
scene.cpp
scene_xrender.cpp
scene_opengl.cpp
eglonxbackend.cpp
glxbackend.cpp
thumbnailitem.cpp
lanczosfilter.cpp
deleted.cpp

View File

@ -1678,7 +1678,7 @@ EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint
m_selection.setEnabledBorders(Plasma::FrameSvg::AllBorders);
if (effects->compositingType() == OpenGLCompositing) {
m_sceneFrame = new SceneOpenGL::EffectFrame(this);
m_sceneFrame = new SceneOpenGL::EffectFrame(this, static_cast<SceneOpenGL*>(Compositor::self()->scene()));
} else if (effects->compositingType() == XRenderCompositing) {
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
m_sceneFrame = new SceneXrender::EffectFrame(this);

View File

@ -2,7 +2,7 @@
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2010 Martin Gräßlin <kde@martin-graesslin.com>
Copyright (C) 2010, 2012 Martin Gräßlin <mgraesslin@kde.org>
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
@ -17,29 +17,53 @@ 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/>.
*********************************************************************/
#ifdef KWIN_HAVE_OPENGLES
#include "eglonxbackend.h"
// kwin
#include "options.h"
#include "overlaywindow.h"
// kwin libs
#include <kwinglplatform.h>
// 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)
namespace KWin
{
if (!initRenderingContext())
EglOnXBackend::EglOnXBackend()
: OpenGLBackend()
, surfaceHasSubPost(0)
{
init();
// Egl is always direct rendering
setIsDirectRendering(true);
// Egl is always double buffered
setDoubleBuffer(true);
}
EglOnXBackend::~EglOnXBackend()
{
cleanupGL();
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(dpy, ctx);
eglDestroySurface(dpy, surface);
eglTerminate(dpy);
eglReleaseThread();
if (overlayWindow()->window()) {
overlayWindow()->destroy();
}
}
void EglOnXBackend::init()
{
if (!initRenderingContext()) {
setFailed("Could not initialize rendering context");
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";
setFailed("Required support for binding pixmaps to EGLImages not found, disabling compositing");
return;
}
GLPlatform *glPlatform = GLPlatform::instance();
@ -47,21 +71,9 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
glPlatform->printResults();
initGL();
if (!hasGLExtension("GL_OES_EGL_image")) {
kError(1212) << "Required extension GL_OES_EGL_image not found, disabling compositing";
setFailed("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;
// TODO: activate once this is resolved. currently the explicit invocation seems pointless
#if 0
@ -78,35 +90,8 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
#endif
}
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()
bool EglOnXBackend::initRenderingContext()
{
dpy = eglGetDisplay(display());
if (dpy == EGL_NO_DISPLAY)
@ -116,13 +101,13 @@ bool SceneOpenGL::initRenderingContext()
return false;
eglBindAPI(EGL_OPENGL_ES_API);
initBufferConfigs();
if (!m_overlayWindow->create()) {
if (!overlayWindow()->create()) {
kError(1212) << "Could not get overlay window";
return false;
} else {
m_overlayWindow->setup(None);
overlayWindow()->setup(None);
}
surface = eglCreateWindowSurface(dpy, config, m_overlayWindow->window(), 0);
surface = eglCreateWindowSurface(dpy, config, overlayWindow()->window(), 0);
eglSurfaceAttrib(dpy, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
@ -147,12 +132,7 @@ bool SceneOpenGL::initRenderingContext()
return true;
}
bool SceneOpenGL::initBuffer()
{
return false;
}
bool SceneOpenGL::initBufferConfigs()
bool EglOnXBackend::initBufferConfigs()
{
const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
@ -183,127 +163,113 @@ bool SceneOpenGL::initBufferConfigs()
return true;
}
bool SceneOpenGL::initDrawableConfigs()
void EglOnXBackend::flushBuffer()
{
return false;
}
// the entry function for painting
int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
if (!m_lastDamage.isEmpty())
flushBuffer(m_lastMask, m_lastDamage);
m_renderTimer.start();
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
}
XSync(display(), false);
int mask = 0;
paintScreen(&mask, &damage); // call generic implementation
m_lastMask = mask;
m_lastDamage = damage;
glFlush();
if (m_overlayWindow->window()) // show the window only after the first pass, since
m_overlayWindow->show(); // that pass may take long
// do cleanup
stacking_order.clear();
checkGLError("PostPaint");
return m_renderTimer.elapsed();
}
void SceneOpenGL::waitSync()
{
// not used in EGL
}
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
{
if (mask & PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV) {
QRect damageRect = damage.boundingRect();
if (lastMask() & Scene::PAINT_SCREEN_REGION && surfaceHasSubPost && eglPostSubBufferNV) {
const QRect damageRect = lastDamage().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)
void EglOnXBackend::screenGeometryChanged(const QSize &size)
{
glViewport(0,0, size.width(), size.height());
Scene::screenGeometryChanged(size);
ShaderManager::instance()->resetAllShaders();
Q_UNUSED(size)
// no backend specific code needed
// TODO: base implementation in OpenGLBackend
}
//****************************************
// SceneOpenGL::Texture
//****************************************
SceneOpenGL::TexturePrivate *EglOnXBackend::createBackendTexture(SceneOpenGL::Texture *texture)
{
return new EglTexture(texture, this);
}
SceneOpenGL::TexturePrivate::TexturePrivate()
void KWin::EglOnXBackend::prepareRenderingFrame()
{
if (!lastDamage().isEmpty())
flushBuffer();
startRenderTimer();
}
void KWin::EglOnXBackend::endRenderingFrame(int mask, const QRegion &damage)
{
setLastDamage(damage);
setLastMask(mask);
glFlush();
if (overlayWindow()->window()) // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long
}
/************************************************
* EglTexture
************************************************/
EglTexture::EglTexture(KWin::SceneOpenGL::Texture *texture, KWin::EglOnXBackend *backend)
: SceneOpenGL::TexturePrivate()
, q(texture)
, m_backend(backend)
, m_image(EGL_NO_IMAGE_KHR)
{
m_target = GL_TEXTURE_2D;
m_image = EGL_NO_IMAGE_KHR;
}
SceneOpenGL::TexturePrivate::~TexturePrivate()
EglTexture::~EglTexture()
{
if (m_image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(dpy, m_image);
eglDestroyImageKHR(m_backend->dpy, m_image);
}
}
void SceneOpenGL::Texture::findTarget()
OpenGLBackend *EglTexture::backend()
{
Q_D(Texture);
d->m_target = GL_TEXTURE_2D;
return m_backend;
}
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
int depth, QRegion region)
void EglTexture::findTarget()
{
// decrease the reference counter for the old texture
d_ptr = new TexturePrivate();
if (m_target != GL_TEXTURE_2D) {
m_target = GL_TEXTURE_2D;
}
}
Q_D(Texture);
bool EglTexture::loadTexture(const Pixmap &pix, const QSize &size, int depth)
{
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();
glGenTextures(1, &m_texture);
q->setWrapMode(GL_CLAMP_TO_EDGE);
q->setFilter(GL_LINEAR);
q->bind();
const EGLint attribs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE
};
d->m_image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
m_image = eglCreateImageKHR(m_backend->dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
(EGLClientBuffer)pix, attribs);
if (EGL_NO_IMAGE_KHR == d->m_image) {
if (EGL_NO_IMAGE_KHR == m_image) {
kDebug(1212) << "failed to create egl image";
unbind();
discard();
q->unbind();
q->discard();
return false;
}
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->m_image);
unbind();
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)m_image);
q->unbind();
checkGLError("load texture");
setYInverted(true);
d->m_size = size;
q->setYInverted(true);
m_size = size;
return true;
}
void SceneOpenGL::TexturePrivate::onDamage()
void KWin::EglTexture::onDamage()
{
if (options->isGlStrictBinding()) {
// This is just implemented to be consistent with
@ -313,3 +279,6 @@ void SceneOpenGL::TexturePrivate::onDamage()
}
GLTexturePrivate::onDamage();
}
} // namespace
#endif

77
eglonxbackend.h Normal file
View File

@ -0,0 +1,77 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
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/>.
*********************************************************************/
#ifndef KWIN_EGL_ON_X_BACKEND_H
#define KWIN_EGL_ON_X_BACKEND_H
#include "scene_opengl.h"
namespace KWin
{
/**
* @brief OpenGL Backend using Egl windowing system over an X overlay window.
**/
class EglOnXBackend : public OpenGLBackend
{
public:
EglOnXBackend();
virtual ~EglOnXBackend();
virtual void screenGeometryChanged(const QSize &size);
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
virtual void prepareRenderingFrame();
virtual void endRenderingFrame(int mask, const QRegion &damage);
protected:
virtual void flushBuffer();
private:
void init();
bool initBufferConfigs();
bool initRenderingContext();
EGLDisplay dpy;
EGLConfig config;
EGLSurface surface;
EGLContext ctx;
int surfaceHasSubPost;
friend class EglTexture;
};
/**
* @brief Texture using an EGLImageKHR.
**/
class EglTexture : public SceneOpenGL::TexturePrivate
{
public:
virtual ~EglTexture();
virtual void onDamage();
virtual void findTarget();
virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth);
virtual OpenGLBackend *backend();
private:
friend class EglOnXBackend;
EglTexture(SceneOpenGL::Texture *texture, EglOnXBackend *backend);
SceneOpenGL::Texture *q;
EglOnXBackend *m_backend;
EGLImageKHR m_image;
};
} // namespace
#endif // KWIN_EGL_ON_X_BACKEND_H

View File

@ -3,6 +3,7 @@
This file is part of the KDE project.
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
Based on glcompmgr code by Felix Bellaby.
Using code from Compiz and Beryl.
@ -21,63 +22,77 @@ 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
// 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>
// 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;
namespace KWin
{
GlxBackend::GlxBackend()
: OpenGLBackend()
, glxbuffer(None)
{
init();
}
SceneOpenGL::SceneOpenGL(Workspace* ws)
: Scene(ws)
, m_resetModelViewProjectionMatrix(true)
, init_ok(false)
GlxBackend::~GlxBackend()
{
// TODO: cleanup in error case
// do cleanup after initBuffer()
cleanupGL();
glXMakeCurrent(display(), None, NULL);
glXDestroyContext(display(), ctxbuffer);
if (overlayWindow()->window()) {
if (hasGLXVersion(1, 3))
glXDestroyWindow(display(), glxbuffer);
XDestroyWindow(display(), buffer);
overlayWindow()->destroy();
} else {
glXDestroyPixmap(display(), glxbuffer);
XFreeGC(display(), gcroot);
XFreePixmap(display(), buffer);
}
checkGLError("Cleanup");
}
void GlxBackend::init()
{
initGLX();
// check for FBConfig support
if (!hasGLExtension("GLX_SGIX_fbconfig") || !glXGetFBConfigAttrib || !glXGetFBConfigs ||
!glXGetVisualFromFBConfig || !glXCreatePixmap || !glXDestroyPixmap ||
!glXCreateWindow || !glXDestroyWindow) {
kError(1212) << "GLX_SGIX_fbconfig or required GLX functions missing";
setFailed("GLX_SGIX_fbconfig or required GLX functions missing");
return; // error
}
if (!selectMode())
return; // error
if (!initBuffer()) // create destination buffer
return; // error
if (!initRenderingContext())
return; // error
if (!initDrawableConfigs()) {
setFailed("Could not initialize the drawable configs");
return;
}
if (!initBuffer()) {
setFailed("Could not initialize the buffer");
return;
}
if (!initRenderingContext()) {
setFailed("Could not initialize rendering context");
return;
}
// Initialize OpenGL
GLPlatform *glPlatform = GLPlatform::instance();
glPlatform->detect();
glPlatform->printResults();
initGL();
if (glPlatform->isSoftwareEmulation()) {
kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender.";
QTimer::singleShot(0, Workspace::self(), SLOT(fallbackToXRenderCompositing()));
return;
}
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";
return; // error
}
if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(7, 10)) {
kError(1212) << "KWin requires at least Mesa 7.10 for OpenGL compositing.";
return;
}
if (db)
glDrawBuffer(GL_BACK);
// Check whether certain features are supported
has_waitSync = false;
if (options->isGlVSync()) {
if (glXGetVideoSync && glXSwapInterval && glXIsDirect(display(), ctxbuffer)) {
unsigned int sync;
@ -88,7 +103,7 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
// (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
has_waitSync = true;
setHasWaitSync(true);
glXSwapInterval(0);
}
else
@ -99,92 +114,12 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
qWarning() << "NO VSYNC! glXGetVideoSync, glXSwapInterval, glXIsDirect" <<
bool(glXGetVideoSync) << bool(glXSwapInterval) << glXIsDirect(display(), ctxbuffer);
}
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
// scene shader setup
if (GLPlatform::instance()->supports(GLSL)) {
if (!ShaderManager::instance()->isValid()) {
kDebug(1212) << "No Scene Shaders available";
} else {
// push one shader on the stack so that one is always bound
// consistency with GLES
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
}
}
// OpenGL scene setup
setupModelViewProjectionMatrix();
if (checkGLError("Init")) {
kError(1212) << "OpenGL compositing setup failed";
return; // error
}
// set strict binding
if (options->isGlStrictBindingFollowsDriver()) {
options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
}
kDebug(1212) << "DB:" << db << ", Direct:" << bool(glXIsDirect(display(), ctxbuffer)) << endl;
init_ok = true;
setIsDirectRendering(bool(glXIsDirect(display(), ctxbuffer)));
kDebug(1212) << "DB:" << isDoubleBuffer() << ", Direct:" << isDirectRendering() << endl;
}
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();
glXMakeCurrent(display(), None, NULL);
glXDestroyContext(display(), ctxbuffer);
if (m_overlayWindow->window()) {
if (hasGLXVersion(1, 3))
glXDestroyWindow(display(), glxbuffer);
XDestroyWindow(display(), buffer);
m_overlayWindow->destroy();
} else {
glXDestroyPixmap(display(), glxbuffer);
XFreeGC(display(), gcroot);
XFreePixmap(display(), buffer);
}
SceneOpenGL::EffectFrame::cleanup();
checkGLError("Cleanup");
}
void SceneOpenGL::setupModelViewProjectionMatrix()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
float ymax = zNear * tan(fovy * M_PI / 360.0f);
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
// swap top and bottom to have OpenGL coordinate system match X system
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
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);
m_resetModelViewProjectionMatrix = false;
}
bool SceneOpenGL::initTfp()
{
if (glXBindTexImageEXT == NULL || glXReleaseTexImageEXT == NULL)
return false;
return true;
}
bool SceneOpenGL::initRenderingContext()
bool GlxBackend::initRenderingContext()
{
bool direct_rendering = options->isGlDirect();
KXErrorHandler errs1;
@ -217,25 +152,24 @@ bool SceneOpenGL::initRenderingContext()
return true;
}
// create destination buffer
bool SceneOpenGL::initBuffer()
bool GlxBackend::initBuffer()
{
if (!initBufferConfigs())
return false;
if (fbcbuffer_db != NULL && m_overlayWindow->create()) {
if (fbcbuffer_db != NULL && overlayWindow()->create()) {
// we have overlay, try to create double-buffered window in it
fbcbuffer = fbcbuffer_db;
XVisualInfo* visual = glXGetVisualFromFBConfig(display(), fbcbuffer);
XSetWindowAttributes attrs;
attrs.colormap = XCreateColormap(display(), rootWindow(), visual->visual, AllocNone);
buffer = XCreateWindow(display(), m_overlayWindow->window(), 0, 0, displayWidth(), displayHeight(),
buffer = XCreateWindow(display(), overlayWindow()->window(), 0, 0, displayWidth(), displayHeight(),
0, visual->depth, InputOutput, visual->visual, CWColormap, &attrs);
if (hasGLXVersion(1, 3))
glxbuffer = glXCreateWindow(display(), fbcbuffer, buffer, NULL);
else
glxbuffer = buffer;
m_overlayWindow->setup(buffer);
db = true;
overlayWindow()->setup(buffer);
setDoubleBuffer(true);
XFree(visual);
} else if (fbcbuffer_nondb != NULL) {
// cannot get any double-buffered drawable, will double-buffer using a pixmap
@ -247,7 +181,7 @@ bool SceneOpenGL::initBuffer()
buffer = XCreatePixmap(display(), rootWindow(), displayWidth(), displayHeight(),
visual->depth);
glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL);
db = false;
setDoubleBuffer(false);
XFree(visual);
} else {
kError(1212) << "Couldn't create output buffer (failed to create overlay window?) !";
@ -261,8 +195,7 @@ bool SceneOpenGL::initBuffer()
return true;
}
// choose the best configs for the destination buffer
bool SceneOpenGL::initBufferConfigs()
bool GlxBackend::initBufferConfigs()
{
int cnt;
GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt);
@ -348,8 +281,7 @@ bool SceneOpenGL::initBufferConfigs()
return true;
}
// make a list of the best configs for windows by depth
bool SceneOpenGL::initDrawableConfigs()
bool GlxBackend::initDrawableConfigs()
{
int cnt;
GLXFBConfig *fbconfigs = glXGetFBConfigs(display(), DefaultScreen(display()), &cnt);
@ -456,54 +388,14 @@ bool SceneOpenGL::initDrawableConfigs()
return true;
}
// the entry function for painting
int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
if (!m_lastDamage.isEmpty())
flushBuffer(m_lastMask, m_lastDamage);
// actually paint the frame, flushed with the NEXT frame
foreach (Toplevel * c, toplevels) {
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
}
glXWaitX();
if (m_resetModelViewProjectionMatrix) {
// reset model view projection matrix if required
setupModelViewProjectionMatrix();
}
int mask = 0;
#ifdef CHECK_GL_ERROR
checkGLError("Paint1");
#endif
paintScreen(&mask, &damage); // call generic implementation
m_lastMask = mask;
m_lastDamage = damage;
#ifdef CHECK_GL_ERROR
checkGLError("Paint2");
#endif
glFlush();
if (m_overlayWindow->window()) // show the window only after the first pass,
m_overlayWindow->show(); // since that pass may take long
// do cleanup
stacking_order.clear();
checkGLError("PostPaint");
return m_renderTimer.elapsed();
}
#define VSYNC_DEBUG 0
// wait for vblank signal before painting
void SceneOpenGL::waitSync()
void GlxBackend::waitSync()
{
// NOTE that vsync has no effect with indirect rendering
if (waitSyncAvailable()) {
#if VSYNC_DEBUG
m_renderTimer.start();
startRenderTimer();
#endif
uint sync;
#if 0
@ -517,9 +409,9 @@ void SceneOpenGL::waitSync()
#endif
#if VSYNC_DEBUG
static int waitTime = 0, waitCounter = 0, doubleSyncCounter = 0;
if (m_renderTimer.elapsed() > 11)
if (renderTime() > 11)
++doubleSyncCounter;
waitTime += m_renderTimer.elapsed();
waitTime += renderTime();
++waitCounter;
if (waitCounter > 99)
{
@ -528,19 +420,18 @@ void SceneOpenGL::waitSync()
}
#endif
}
m_renderTimer.start(); // yes, the framerate shall be constant anyway.
startRenderTimer(); // yes, the framerate shall be constant anyway.
}
#undef VSYNC_DEBUG
// actually paint to the screen (double-buffer swap or copy from pixmap buffer)
void SceneOpenGL::flushBuffer(int mask, QRegion damage)
void GlxBackend::flushBuffer()
{
if (db) {
if (mask & PAINT_SCREEN_REGION) {
if (isDoubleBuffer()) {
if (lastMask() & Scene::PAINT_SCREEN_REGION) {
waitSync();
if (glXCopySubBuffer) {
foreach (const QRect & r, damage.rects()) {
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
glXCopySubBuffer(display(), glxbuffer, r.x(), y, r.width(), r.height());
@ -563,7 +454,7 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
glDrawBuffer(GL_FRONT);
int xpos = 0;
int ypos = 0;
foreach (const QRect & r, damage.rects()) {
foreach (const QRect & r, lastDamage().rects()) {
// convert to OpenGL coordinates
int y = displayHeight() - r.y() - r.height();
// Move raster position relatively using glBitmap() rather
@ -592,7 +483,7 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
glXSwapInterval(options->isGlVSync() ? 1 : 0);
glXSwapBuffers(display(), glxbuffer);
glXSwapInterval(0);
m_renderTimer.start(); // this is important so we don't assume to be loosing frames in the compositor timing calculation
startRenderTimer(); // this is important so we don't assume to be loosing frames in the compositor timing calculation
} else {
waitSync();
glXSwapBuffers(display(), glxbuffer);
@ -601,8 +492,8 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
glXWaitGL();
} else {
glXWaitGL();
if (mask & PAINT_SCREEN_REGION)
foreach (const QRect & r, damage.rects())
if (lastMask() & Scene::PAINT_SCREEN_REGION)
foreach (const QRect & r, lastDamage().rects())
XCopyArea(display(), buffer, rootWindow(), gcroot, r.x(), r.y(), r.width(), r.height(), r.x(), r.y());
else
XCopyArea(display(), buffer, rootWindow(), gcroot, 0, 0, displayWidth(), displayHeight(), 0, 0);
@ -610,11 +501,9 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
XFlush(display());
}
void SceneOpenGL::screenGeometryChanged(const QSize &size)
void GlxBackend::screenGeometryChanged(const QSize &size)
{
Scene::screenGeometryChanged(size);
glViewport(0,0, size.width(), size.height());
if (m_overlayWindow->window() == None) {
if (overlayWindow()->window() == None) {
glXMakeCurrent(display(), None, NULL);
glXDestroyPixmap(display(), glxbuffer);
XFreePixmap(display(), buffer);
@ -624,29 +513,51 @@ void SceneOpenGL::screenGeometryChanged(const QSize &size)
glxbuffer = glXCreatePixmap(display(), fbcbuffer, buffer, NULL);
glXMakeCurrent(display(), glxbuffer, ctxbuffer);
// TODO: there seems some bug, some clients become black until an eg. un/remap - could be a general pixmap buffer issue, though
}
else {
} else {
glXMakeCurrent(display(), None, NULL); // deactivate context ////
XMoveResizeWindow(display(), buffer, 0,0, size.width(), size.height());
m_overlayWindow->setup(buffer);
overlayWindow()->setup(buffer);
XSync(display(), false); // ensure X11 stuff has applied ////
glXMakeCurrent(display(), glxbuffer, ctxbuffer); // reactivate context ////
glViewport(0,0, size.width(), size.height()); // adjust viewport last - should btw. be superfluous on the Pixmap buffer - iirc glXCreatePixmap sets the context anyway. ////
}
ShaderManager::instance()->resetAllShaders();
m_resetModelViewProjectionMatrix = true;
}
//****************************************
// SceneOpenGL::Texture
//****************************************
SceneOpenGL::TexturePrivate::TexturePrivate()
SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture)
{
m_glxpixmap = None;
return new GlxTexture(texture, this);
}
SceneOpenGL::TexturePrivate::~TexturePrivate()
void GlxBackend::prepareRenderingFrame()
{
if (!lastDamage().isEmpty())
flushBuffer();
glXWaitX();
}
void GlxBackend::endRenderingFrame(int mask, const QRegion &damage)
{
setLastDamage(damage);
setLastMask(mask);
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()
{
if (m_glxpixmap != None) {
if (!options->isGlStrictBinding()) {
@ -657,112 +568,7 @@ SceneOpenGL::TexturePrivate::~TexturePrivate()
}
}
void SceneOpenGL::Texture::findTarget()
{
Q_D(Texture);
unsigned int new_target = 0;
if (glXQueryDrawable && d->m_glxpixmap != None)
glXQueryDrawable(display(), d->m_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(d->m_size.width()) && isPowerOfTwo(d->m_size.height()))) {
new_target = GLX_TEXTURE_2D_EXT;
} else {
new_target = GLX_TEXTURE_RECTANGLE_EXT;
}
}
switch(new_target) {
case GLX_TEXTURE_2D_EXT:
d->m_target = GL_TEXTURE_2D;
d->m_scale.setWidth(1.0f / d->m_size.width());
d->m_scale.setHeight(1.0f / d->m_size.height());
break;
case GLX_TEXTURE_RECTANGLE_EXT:
d->m_target = GL_TEXTURE_RECTANGLE_ARB;
d->m_scale.setWidth(1.0f);
d->m_scale.setHeight(1.0f);
break;
default:
abort();
}
}
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
int depth, QRegion region)
{
Q_UNUSED(region)
// decrease the reference counter for the old texture
d_ptr = new TexturePrivate();
Q_D(Texture);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad1");
#endif
if (pix == None || size.isEmpty() || depth < 1)
return false;
if (fbcdrawableinfo[ depth ].fbconfig == NULL) {
kDebug(1212) << "No framebuffer configuration for depth " << depth
<< "; not binding pixmap" << endl;
return false;
}
d->m_size = size;
// new texture, or texture contents changed; mipmaps now invalid
setDirty();
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad2");
#endif
// tfp mode, simply bind the pixmap to texture
glGenTextures(1, &d->m_texture);
// 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.
int attrs[] = {
GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap > 0,
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) {
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;
} else if (fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
}
}
d->m_glxpixmap = glXCreatePixmap(display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoadTFP1");
#endif
findTarget();
d->m_yInverted = fbcdrawableinfo[ depth ].y_inverted ? true : false;
d->m_canUseMipmaps = fbcdrawableinfo[ depth ].mipmap > 0;
setFilter(fbcdrawableinfo[ depth ].mipmap > 0 ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST);
glBindTexture(d->m_target, d->m_texture);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoadTFP2");
#endif
glXBindTexImageEXT(display(), d->m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad0");
#endif
unbind();
return true;
}
void SceneOpenGL::TexturePrivate::onDamage()
void GlxTexture::onDamage()
{
if (options->isGlStrictBinding() && m_glxpixmap) {
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
@ -770,3 +576,109 @@ void SceneOpenGL::TexturePrivate::onDamage()
}
GLTexturePrivate::onDamage();
}
void GlxTexture::findTarget()
{
unsigned int new_target = 0;
if (glXQueryDrawable && m_glxpixmap != None)
glXQueryDrawable(display(), m_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 (GLTexture::NPOTTextureSupported() ||
(isPowerOfTwo(m_size.width()) && isPowerOfTwo(m_size.height()))) {
new_target = GLX_TEXTURE_2D_EXT;
} else {
new_target = GLX_TEXTURE_RECTANGLE_EXT;
}
}
switch(new_target) {
case GLX_TEXTURE_2D_EXT:
m_target = GL_TEXTURE_2D;
m_scale.setWidth(1.0f / m_size.width());
m_scale.setHeight(1.0f / m_size.height());
break;
case GLX_TEXTURE_RECTANGLE_EXT:
m_target = GL_TEXTURE_RECTANGLE_ARB;
m_scale.setWidth(1.0f);
m_scale.setHeight(1.0f);
break;
default:
abort();
}
}
bool GlxTexture::loadTexture(const Pixmap& pix, const QSize& size, int depth)
{
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad1");
#endif
if (pix == None || size.isEmpty() || depth < 1)
return false;
if (m_backend->fbcdrawableinfo[ depth ].fbconfig == NULL) {
kDebug(1212) << "No framebuffer configuration for depth " << depth
<< "; not binding pixmap" << endl;
return false;
}
m_size = size;
// new texture, or texture contents changed; mipmaps now invalid
q->setDirty();
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad2");
#endif
// tfp mode, simply bind the pixmap to texture
glGenTextures(1, &m_texture);
// 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.
int attrs[] = {
GLX_TEXTURE_FORMAT_EXT, m_backend->fbcdrawableinfo[ depth ].bind_texture_format,
GLX_MIPMAP_TEXTURE_EXT, m_backend->fbcdrawableinfo[ depth ].mipmap > 0,
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) {
if ((m_backend->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;
} else if (m_backend->fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_EXT;
}
}
m_glxpixmap = glXCreatePixmap(display(), m_backend->fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoadTFP1");
#endif
findTarget();
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);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoadTFP2");
#endif
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
#ifdef CHECK_GL_ERROR
checkGLError("TextureLoad0");
#endif
unbind();
return true;
}
OpenGLBackend *GlxTexture::backend()
{
return m_backend;
}
} // namespace
#endif

93
glxbackend.h Normal file
View File

@ -0,0 +1,93 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
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/>.
*********************************************************************/
#ifndef KWIN_GLX_BACKEND_H
#define KWIN_GLX_BACKEND_H
#include "scene_opengl.h"
namespace KWin
{
class FBConfigInfo
{
public:
GLXFBConfig fbconfig;
int bind_texture_format;
int texture_targets;
int y_inverted;
int mipmap;
};
/**
* @brief OpenGL Backend using GLX over an X overlay window.
**/
class GlxBackend : public OpenGLBackend
{
public:
GlxBackend();
virtual ~GlxBackend();
virtual void screenGeometryChanged(const QSize &size);
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture);
virtual void prepareRenderingFrame();
virtual void endRenderingFrame(int mask, const QRegion &damage);
protected:
virtual void flushBuffer();
private:
void init();
bool initBuffer();
bool initDrawableConfigs();
void waitSync();
bool initRenderingContext();
bool initBufferConfigs();
GC gcroot;
Drawable buffer;
GLXFBConfig fbcbuffer_db;
GLXFBConfig fbcbuffer_nondb;
FBConfigInfo fbcdrawableinfo[ 32 + 1 ];
GLXFBConfig fbcbuffer;
GLXDrawable glxbuffer;
GLXContext ctxbuffer;
friend class GlxTexture;
};
/**
* @brief Texture using an GLXPixmap.
**/
class GlxTexture : public SceneOpenGL::TexturePrivate
{
public:
virtual ~GlxTexture();
virtual void onDamage();
virtual void findTarget();
virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth);
virtual OpenGLBackend *backend();
private:
friend class GlxBackend;
GlxTexture(SceneOpenGL::Texture *texture, GlxBackend *backend);
SceneOpenGL::Texture *q;
GlxBackend *m_backend;
GLXPixmap m_glxpixmap; // the glx pixmap the texture is bound to
};
} // namespace
#endif // KWIN_GLX_BACKEND_H

View File

@ -96,8 +96,6 @@ namespace KWin
Scene::Scene(Workspace* ws)
: QObject(ws)
, wspace(ws)
, has_waitSync(false)
, m_overlayWindow(new OverlayWindow())
{
last_time.invalidate(); // Initialize the timer
connect(Workspace::self(), SIGNAL(deletedRemoved(KWin::Deleted*)), SLOT(windowDeleted(KWin::Deleted*)));
@ -105,7 +103,6 @@ Scene::Scene(Workspace* ws)
Scene::~Scene()
{
delete m_overlayWindow;
}
// returns mask and possibly modified region
@ -448,14 +445,14 @@ void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, Windo
w->sceneWindow()->performPaint(mask, region, data);
}
OverlayWindow* Scene::overlayWindow()
bool Scene::waitSyncAvailable() const
{
return m_overlayWindow;
return false;
}
void Scene::screenGeometryChanged(const QSize &size)
{
m_overlayWindow->resize(size);
overlayWindow()->resize(size);
}
//****************************************

View File

@ -96,10 +96,8 @@ public:
enum ImageFilterType { ImageFilterFast, ImageFilterGood };
// there's nothing to paint (adjust time_diff later)
virtual void idle();
bool waitSyncAvailable() {
return has_waitSync;
}
OverlayWindow* overlayWindow();
virtual bool waitSyncAvailable() const;
virtual OverlayWindow* overlayWindow() = 0;
public Q_SLOTS:
// a window has been destroyed
virtual void windowDeleted(KWin::Deleted*) = 0;
@ -156,9 +154,7 @@ protected:
int time_diff;
QElapsedTimer last_time;
Workspace* wspace;
bool has_waitSync;
QWeakPointer<LanczosFilter> lanczos_filter;
OverlayWindow* m_overlayWindow;
};
// The base class for windows representations in composite backends

View File

@ -67,6 +67,11 @@ Sources and other compositing managers:
*/
#include "scene_opengl.h"
#ifdef KWIN_HAVE_OPENGLES
#include "eglonxbackend.h"
#else
#include "glxbackend.h"
#endif
#include <kxerrorhandler.h>
@ -99,18 +104,128 @@ extern int currentRefreshRate();
//****************************************
// SceneOpenGL
//****************************************
OpenGLBackend::OpenGLBackend()
: m_overlayWindow(new OverlayWindow()) // TODO: maybe create only if needed?
, m_waitSync(false)
, m_directRendering(false)
, m_doubleBuffer(false)
, m_failed(false)
, m_lastMask(0)
{
}
bool SceneOpenGL::db; // destination drawable is double-buffered
OpenGLBackend::~OpenGLBackend()
{
if (isFailed()) {
m_overlayWindow->destroy();
}
delete m_overlayWindow;
}
void OpenGLBackend::setFailed(const QString &reason)
{
kWarning(1212) << "Creating the OpenGL rendering failed: " << reason;
m_failed = true;
}
void OpenGLBackend::idle()
{
flushBuffer();
}
/************************************************
* SceneOpenGL
***********************************************/
SceneOpenGL::SceneOpenGL(Workspace* ws)
: Scene(ws)
, m_resetModelViewProjectionMatrix(true)
, init_ok(false)
#ifdef KWIN_HAVE_OPENGLES
#include "scene_opengl_egl.cpp"
, m_backend(new EglOnXBackend())
#else
#include "scene_opengl_glx.cpp"
, m_backend(new GlxBackend())
#endif
{
if (m_backend->isFailed()) {
return;
}
// perform Scene specific checks
GLPlatform *glPlatform = GLPlatform::instance();
if (glPlatform->isSoftwareEmulation()) {
kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender.";
QTimer::singleShot(0, Workspace::self(), SLOT(fallbackToXRenderCompositing()));
return;
}
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";
return; // error
}
if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(7, 10)) {
kError(1212) << "KWin requires at least Mesa 7.10 for OpenGL compositing.";
return;
}
#ifndef KWIN_HAVE_OPENGLES
if (m_backend->isDoubleBuffer())
glDrawBuffer(GL_BACK);
#endif
debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
// scene shader setup
if (GLPlatform::instance()->supports(GLSL)) {
if (!ShaderManager::instance()->isValid()) {
kDebug(1212) << "No Scene Shaders available";
#ifdef KWIN_HAVE_OPENGLES
// with OpenGL ES we need shaders, so let's break here
return;
#endif
} else {
// push one shader on the stack so that one is always bound
ShaderManager::instance()->pushShader(ShaderManager::SimpleShader);
}
}
// OpenGL scene setup
setupModelViewProjectionMatrix();
if (checkGLError("Init")) {
kError(1212) << "OpenGL compositing setup failed";
return; // error
}
// set strict binding
if (options->isGlStrictBindingFollowsDriver()) {
options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
}
init_ok = true;
}
SceneOpenGL::~SceneOpenGL()
{
delete m_backend;
foreach (Window * w, windows) {
delete w;
}
// do cleanup after initBuffer()
SceneOpenGL::EffectFrame::cleanup();
checkGLError("Cleanup");
}
OverlayWindow *SceneOpenGL::overlayWindow()
{
return m_backend->overlayWindow();
}
bool SceneOpenGL::waitSyncAvailable() const
{
return m_backend->waitSyncAvailable();
}
void SceneOpenGL::idle()
{
flushBuffer(m_lastMask, m_lastDamage);
m_backend->idle();
Scene::idle();
}
@ -119,11 +234,59 @@ bool SceneOpenGL::initFailed() const
return !init_ok;
}
bool SceneOpenGL::selectMode()
int SceneOpenGL::paint(QRegion damage, ToplevelList toplevels)
{
if (!initDrawableConfigs())
return false;
return true;
// actually paint the frame, flushed with the NEXT frame
foreach (Toplevel * c, toplevels) {
// TODO: cache the stacking_order in case it has not changed
assert(windows.contains(c));
stacking_order.append(windows[ c ]);
}
m_backend->prepareRenderingFrame();
if (m_resetModelViewProjectionMatrix) {
// reset model view projection matrix if required
setupModelViewProjectionMatrix();
}
int mask = 0;
#ifdef CHECK_GL_ERROR
checkGLError("Paint1");
#endif
paintScreen(&mask, &damage); // call generic implementation
#ifdef CHECK_GL_ERROR
checkGLError("Paint2");
#endif
m_backend->endRenderingFrame(mask, damage);
// do cleanup
stacking_order.clear();
checkGLError("PostPaint");
return m_backend->renderTime();
}
void SceneOpenGL::setupModelViewProjectionMatrix()
{
#ifndef KWIN_HAVE_OPENGLES
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
float ymax = zNear * tan(fovy * M_PI / 360.0f);
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
// swap top and bottom to have OpenGL coordinate system match X system
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
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);
#endif
m_resetModelViewProjectionMatrix = false;
}
QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const
@ -265,30 +428,38 @@ void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t)
#endif
}
SceneOpenGL::Texture *SceneOpenGL::createTexture()
{
return new Texture(m_backend);
}
SceneOpenGL::Texture *SceneOpenGL::createTexture(const QPixmap &pix, GLenum target)
{
return new Texture(m_backend, pix, target);
}
void SceneOpenGL::screenGeometryChanged(const QSize &size)
{
Scene::screenGeometryChanged(size);
glViewport(0,0, size.width(), size.height());
m_backend->screenGeometryChanged(size);
ShaderManager::instance()->resetAllShaders();
#ifndef KWIN_HAVE_OPENGLES
m_resetModelViewProjectionMatrix = true;
#endif
}
//****************************************
// SceneOpenGL::Texture
//****************************************
SceneOpenGL::Texture::Texture() : GLTexture(*new TexturePrivate())
SceneOpenGL::Texture::Texture(OpenGLBackend *backend)
: GLTexture(*backend->createBackendTexture(this))
{
}
SceneOpenGL::Texture::Texture(TexturePrivate& dd) : GLTexture(dd)
{
}
SceneOpenGL::Texture::Texture(const SceneOpenGL::Texture& tex) : GLTexture(*tex.d_ptr)
{
}
SceneOpenGL::Texture::Texture(const Pixmap& pix, const QSize& size, int depth)
: GLTexture(*new TexturePrivate())
{
load(pix, size, depth);
}
SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target)
: GLTexture(*new TexturePrivate())
SceneOpenGL::Texture::Texture(OpenGLBackend *backend, const QPixmap &pix, GLenum target)
: GLTexture(*backend->createBackendTexture(this))
{
load(pix, target);
}
@ -305,7 +476,7 @@ SceneOpenGL::Texture& SceneOpenGL::Texture::operator = (const SceneOpenGL::Textu
void SceneOpenGL::Texture::discard()
{
d_ptr = new TexturePrivate();
d_ptr = d_func()->backend()->createBackendTexture(this);
}
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
@ -338,33 +509,68 @@ bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target)
return load(pixmap.handle(), pixmap.size(), pixmap.depth());
}
void SceneOpenGL::Texture::findTarget()
{
Q_D(Texture);
d->findTarget();
}
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
int depth, QRegion region)
{
Q_UNUSED(region)
// decrease the reference counter for the old texture
d_ptr = d_func()->backend()->createBackendTexture(this); //new TexturePrivate();
Q_D(Texture);
return d->loadTexture(pix, size, depth);
}
//****************************************
// SceneOpenGL::Texture
//****************************************
SceneOpenGL::TexturePrivate::TexturePrivate()
{
}
SceneOpenGL::TexturePrivate::~TexturePrivate()
{
}
//****************************************
// SceneOpenGL::Window
//****************************************
SceneOpenGL::Window::Window(Toplevel* c)
: Scene::Window(c)
, texture()
, topTexture()
, leftTexture()
, rightTexture()
, bottomTexture()
, texture(NULL)
, topTexture(NULL)
, leftTexture(NULL)
, rightTexture(NULL)
, bottomTexture(NULL)
, m_scene(NULL)
{
}
SceneOpenGL::Window::~Window()
{
discardTexture();
delete texture;
delete topTexture;
delete leftTexture;
delete rightTexture;
delete bottomTexture;
}
// Bind the window pixmap to an OpenGL texture.
bool SceneOpenGL::Window::bindTexture()
{
if (!texture.isNull()) {
if (!texture) {
texture = m_scene->createTexture();
}
if (!texture->isNull()) {
if (!toplevel->damage().isEmpty()) {
// mipmaps need to be updated
texture.setDirty();
texture->setDirty();
toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize()));
}
return true;
@ -374,7 +580,7 @@ bool SceneOpenGL::Window::bindTexture()
if (pix == None)
return false;
bool success = texture.load(pix, toplevel->size(), toplevel->depth(),
bool success = texture->load(pix, toplevel->size(), toplevel->depth(),
toplevel->damage());
if (success)
@ -386,11 +592,24 @@ bool SceneOpenGL::Window::bindTexture()
void SceneOpenGL::Window::discardTexture()
{
texture.discard();
topTexture.discard();
leftTexture.discard();
rightTexture.discard();
bottomTexture.discard();
if (texture) {
texture->discard();
}
if (!Extensions::nonNativePixmaps()) {
// only discard if the deco pixmaps use TFP
if (topTexture) {
topTexture->discard();
}
if (leftTexture) {
leftTexture->discard();
}
if (rightTexture) {
rightTexture->discard();
}
if (bottomTexture) {
bottomTexture->discard();
}
}
}
// This call is used in SceneOpenGL::windowGeometryShapeChanged(),
@ -401,14 +620,20 @@ void SceneOpenGL::Window::discardTexture()
// discard the texture only if size changes.
void SceneOpenGL::Window::checkTextureSize()
{
if (texture.size() != size())
if (!texture) {
return;
}
if (texture->size() != size())
discardTexture();
}
// when the window's composite pixmap is discarded, undo binding it to the texture
void SceneOpenGL::Window::pixmapDiscarded()
{
texture.discard();
if (!texture) {
return;
}
texture->discard();
}
QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const
@ -480,7 +705,7 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData
else
filter = ImageFilterFast;
texture.setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST);
texture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST);
bool sceneShader = false;
@ -575,15 +800,15 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData
// paint the content
WindowQuadList contentQuads = data.quads.select(WindowQuadContents);
if (!contentQuads.empty()) {
texture.bind();
texture->bind();
prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.shader);
renderQuads(mask, region, contentQuads, &texture, false, hardwareClipping);
renderQuads(mask, region, contentQuads, texture, false, hardwareClipping);
restoreStates(Content, data.opacity(), data.brightness(), data.saturation(), data.shader);
texture.unbind();
texture->unbind();
#ifndef KWIN_HAVE_OPENGLES
if (m_scene && m_scene->debug) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
renderQuads(mask, region, contentQuads, &texture, false, hardwareClipping);
renderQuads(mask, region, contentQuads, texture, false, hardwareClipping);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
#endif
@ -608,21 +833,33 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType
SceneOpenGL::Texture* decorationTexture;
switch(decorationType) {
case DecorationTop:
decorationTexture = &topTexture;
if (!topTexture) {
topTexture = m_scene->createTexture();
}
decorationTexture = topTexture;
break;
case DecorationLeft:
decorationTexture = &leftTexture;
if (!leftTexture) {
leftTexture = m_scene->createTexture();
}
decorationTexture = leftTexture;
break;
case DecorationRight:
decorationTexture = &rightTexture;
if (!rightTexture) {
rightTexture = m_scene->createTexture();
}
decorationTexture = rightTexture;
break;
case DecorationBottom:
decorationTexture = &bottomTexture;
if (!bottomTexture) {
bottomTexture = m_scene->createTexture();
}
decorationTexture = bottomTexture;
break;
default:
return;
}
if (decoration->isNull()) {
if (decoration->isNull() || !decorationTexture) {
return;
}
if (decorationTexture->isNull() || updateDeco) {
@ -792,19 +1029,19 @@ void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double
Texture *tex = NULL;
switch(type) {
case Content:
tex = &texture;
tex = texture;
break;
case DecorationTop:
tex = &topTexture;
tex = topTexture;
break;
case DecorationLeft:
tex = &leftTexture;
tex = leftTexture;
break;
case DecorationRight:
tex = &rightTexture;
tex = rightTexture;
break;
case DecorationBottom:
tex = &bottomTexture;
tex = bottomTexture;
break;
default:
return;
@ -991,19 +1228,19 @@ void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double
Texture *tex = NULL;
switch(type) {
case Content:
tex = &texture;
tex = texture;
break;
case DecorationTop:
tex = &topTexture;
tex = topTexture;
break;
case DecorationLeft:
tex = &leftTexture;
tex = leftTexture;
break;
case DecorationRight:
tex = &rightTexture;
tex = rightTexture;
break;
case DecorationBottom:
tex = &bottomTexture;
tex = bottomTexture;
break;
default:
return;
@ -1066,10 +1303,10 @@ void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity,
// SceneOpenGL::EffectFrame
//****************************************
SceneOpenGL::Texture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL;
GLTexture* SceneOpenGL::EffectFrame::m_unstyledTexture = NULL;
QPixmap* SceneOpenGL::EffectFrame::m_unstyledPixmap = NULL;
SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame)
SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame, SceneOpenGL *scene)
: Scene::EffectFrame(frame)
, m_texture(NULL)
, m_textTexture(NULL)
@ -1079,6 +1316,7 @@ SceneOpenGL::EffectFrame::EffectFrame(EffectFrameImpl* frame)
, m_oldIconTexture(NULL)
, m_selectionTexture(NULL)
, m_unstyledVBO(NULL)
, m_scene(scene)
{
if (m_effectFrame->style() == EffectFrameUnstyled && !m_unstyledTexture) {
updateUnstyledTexture();
@ -1346,7 +1584,7 @@ void SceneOpenGL::EffectFrame::render(QRegion region, double opacity, double fra
if (!m_selectionTexture) { // Lazy creation
QPixmap pixmap = m_effectFrame->selectionFrame().framePixmap();
if (!pixmap.isNull())
m_selectionTexture = new Texture(pixmap);
m_selectionTexture = m_scene->createTexture(pixmap);
}
if (m_selectionTexture) {
if (shader) {
@ -1403,7 +1641,7 @@ void SceneOpenGL::EffectFrame::render(QRegion region, double opacity, double fra
}
if (!m_iconTexture) { // lazy creation
m_iconTexture = new Texture(m_effectFrame->icon());
m_iconTexture = m_scene->createTexture(m_effectFrame->icon());
}
m_iconTexture->bind();
m_iconTexture->render(region, QRect(topLeft, m_effectFrame->iconSize()));
@ -1462,7 +1700,7 @@ void SceneOpenGL::EffectFrame::updateTexture()
m_texture = 0L;
if (m_effectFrame->style() == EffectFrameStyled) {
QPixmap pixmap = m_effectFrame->frame().framePixmap();
m_texture = new Texture(pixmap);
m_texture = m_scene->createTexture(pixmap);
}
}
@ -1498,7 +1736,7 @@ void SceneOpenGL::EffectFrame::updateTextTexture()
p.setPen(Qt::white);
p.drawText(rect, m_effectFrame->alignment(), text);
p.end();
m_textTexture = new Texture(*m_textPixmap);
m_textTexture = m_scene->createTexture(*m_textPixmap);
}
void SceneOpenGL::EffectFrame::updateUnstyledTexture()
@ -1518,7 +1756,7 @@ void SceneOpenGL::EffectFrame::updateUnstyledTexture()
p.drawEllipse(m_unstyledPixmap->rect());
p.end();
#undef CS
m_unstyledTexture = new Texture(*m_unstyledPixmap);
m_unstyledTexture = new GLTexture(*m_unstyledPixmap);
}
void SceneOpenGL::EffectFrame::cleanup()

View File

@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace KWin
{
class OpenGLBackend;
class SceneOpenGL
: public Scene
@ -46,14 +47,24 @@ public:
virtual CompositingType compositingType() const {
return OpenGLCompositing;
}
virtual bool hasPendingFlush() const { return !m_lastDamage.isEmpty(); }
virtual bool hasPendingFlush() const;
virtual int paint(QRegion damage, ToplevelList windows);
virtual void windowAdded(Toplevel*);
virtual void windowDeleted(Deleted*);
virtual void screenGeometryChanged(const QSize &size);
virtual OverlayWindow *overlayWindow();
virtual bool waitSyncAvailable() const;
void idle();
/**
* @brief Factory method to create a backend specific texture.
*
* @return :SceneOpenGL::Texture*
**/
Texture *createTexture();
Texture *createTexture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
protected:
virtual void paintGenericScreen(int mask, ScreenPaintData data);
virtual void paintBackground(QRegion region);
@ -63,66 +74,27 @@ public Q_SLOTS:
virtual void windowGeometryShapeChanged(KWin::Toplevel* c);
virtual void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted);
private:
bool selectMode();
bool initTfp();
bool initBuffer();
bool initRenderingContext();
bool initBufferConfigs();
bool initDrawableConfigs();
void waitSync();
#ifndef KWIN_HAVE_OPENGLES
void setupModelViewProjectionMatrix();
#endif
void flushBuffer(int mask, QRegion damage);
GC gcroot;
class FBConfigInfo
{
public:
#ifndef KWIN_HAVE_OPENGLES
GLXFBConfig fbconfig;
#endif
int bind_texture_format;
int texture_targets;
int y_inverted;
int mipmap;
};
#ifndef KWIN_HAVE_OPENGLES
Drawable buffer;
GLXFBConfig fbcbuffer;
bool m_resetModelViewProjectionMatrix;
#endif
static bool db;
#ifndef KWIN_HAVE_OPENGLES
static GLXFBConfig fbcbuffer_db;
static GLXFBConfig fbcbuffer_nondb;
static FBConfigInfo fbcdrawableinfo[ 32 + 1 ];
static GLXDrawable glxbuffer;
static GLXContext ctxbuffer;
static GLXContext ctxdrawable;
static GLXDrawable last_pixmap; // for a workaround in bindTexture()
#endif
QHash< Toplevel*, Window* > windows;
bool init_ok;
bool debug;
QElapsedTimer m_renderTimer;
QRegion m_lastDamage;
int m_lastMask;
OpenGLBackend *m_backend;
};
class SceneOpenGL::TexturePrivate
: public GLTexturePrivate
{
public:
TexturePrivate();
virtual ~TexturePrivate();
virtual void onDamage();
virtual void findTarget() = 0;
virtual bool loadTexture(const Pixmap& pix, const QSize& size, int depth) = 0;
virtual OpenGLBackend *backend() = 0;
protected:
TexturePrivate();
#ifndef KWIN_HAVE_OPENGLES
GLXPixmap m_glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode
#else
EGLImageKHR m_image;
#endif
private:
Q_DISABLE_COPY(TexturePrivate)
};
@ -131,9 +103,8 @@ class SceneOpenGL::Texture
: public GLTexture
{
public:
Texture();
Texture(const Texture& tex);
Texture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
Texture(OpenGLBackend *backend);
Texture(OpenGLBackend *backend, const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
virtual ~Texture();
Texture & operator = (const Texture& tex);
@ -144,7 +115,6 @@ public:
virtual void discard();
protected:
Texture(const Pixmap& pix, const QSize& size, int depth);
void findTarget();
virtual bool load(const Pixmap& pix, const QSize& size, int depth,
QRegion region);
@ -198,11 +168,11 @@ protected:
void restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader);
private:
Texture texture;
Texture topTexture;
Texture leftTexture;
Texture rightTexture;
Texture bottomTexture;
Texture *texture;
Texture *topTexture;
Texture *leftTexture;
Texture *rightTexture;
Texture *bottomTexture;
SceneOpenGL *m_scene;
};
@ -210,7 +180,7 @@ class SceneOpenGL::EffectFrame
: public Scene::EffectFrame
{
public:
EffectFrame(EffectFrameImpl* frame);
EffectFrame(EffectFrameImpl* frame, SceneOpenGL *scene);
virtual ~EffectFrame();
virtual void free();
@ -237,8 +207,9 @@ private:
Texture* m_oldIconTexture;
Texture* m_selectionTexture;
GLVertexBuffer* m_unstyledVBO;
SceneOpenGL *m_scene;
static Texture* m_unstyledTexture;
static GLTexture* m_unstyledTexture;
static QPixmap* m_unstyledPixmap; // need to keep the pixmap around to workaround some driver problems
static void updateUnstyledTexture(); // Update OpenGL unstyled frame texture
};
@ -266,6 +237,224 @@ private:
GLTexture *m_texture;
};
/**
* @brief The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
*
* The OpenGLBackend is an abstract base class used by the SceneOpenGL to abstract away the differences
* between various OpenGL windowing systems such as GLX and EGL.
*
* A concrete implementation has to create and release the OpenGL context in a way so that the
* SceneOpenGL does not have to care about it.
*
* In addition a major task for this class is to generate the SceneOpenGL::TexturePrivate which is
* able to perform the texture from pixmap operation in the given backend.
*
* @author Martin Gräßlin <mgraesslin@kde.org>
**/
class OpenGLBackend
{
public:
OpenGLBackend();
virtual ~OpenGLBackend();
/**
* @return Time passes since start of rendering current frame.
* @see startRenderTimer
**/
qint64 renderTime() {
return m_renderTimer.elapsed();
}
virtual void screenGeometryChanged(const QSize &size) = 0;
virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture) = 0;
/**
* @brief Backend specific code to prepare the rendering of a frame including flushing the
* previously rendered frame to the screen if the backend works this way.
**/
virtual void prepareRenderingFrame() = 0;
/**
* @brief Backend specific code to handle the end of rendering a frame.
*
* @param mask The rendering mask of this frame
* @param damage The actual updated region in this frame
**/
virtual void endRenderingFrame(int mask, const QRegion &damage) = 0;
/**
* @brief Compositor is going into idle mode, flushes any pending paints.
**/
void idle();
/**
* @return bool Whether the scene needs to flush a frame.
**/
bool hasPendingFlush() const {
return !m_lastDamage.isEmpty();
}
/**
* @brief Returns the OverlayWindow used by the backend.
*
* A backend does not have to use an OverlayWindow, this is mostly for the X world.
* In case the backend does not use an OverlayWindow it is allowed to return @c null.
* It's the task of the caller to check whether it is @c null.
*
* @return :OverlayWindow*
**/
OverlayWindow *overlayWindow() {
return m_overlayWindow;
}
/**
* @brief Whether the creation of the Backend failed.
*
* The SceneOpenGL should test whether the Backend got constructed correctly. If this method
* returns @c true, the SceneOpenGL should not try to start the rendering.
*
* @return bool @c true if the creation of the Backend failed, @c false otherwise.
**/
bool isFailed() const {
return m_failed;
}
/**
* @brief Whether the Backend provides VSync.
*
* Currently only the GLX backend can provide VSync.
*
* @return bool @c true if VSync support is available, @c false otherwise
**/
bool waitSyncAvailable() const {
return m_waitSync;
}
/**
* @brief Whether the backend uses direct rendering.
*
* Some OpenGLScene modes require direct rendering. E.g. the OpenGL 2 should not be used
* if direct rendering is not supported by the Scene.
*
* @return bool @c true if the GL context is direct, @c false if indirect
**/
bool isDirectRendering() const {
return m_directRendering;
}
/**
* @brief Whether the backend used double buffering.
*
* @return bool @c true if double buffered, @c false otherwise
**/
bool isDoubleBuffer() const {
return m_doubleBuffer;
}
protected:
/**
* @brief Backend specific flushing of frame to screen.
**/
virtual void flushBuffer() = 0;
/**
* @brief Sets the backend initialization to failed.
*
* This method should be called by the concrete subclass in case the initialization failed.
* The given @p reason is logged as a warning.
*
* @param reason The reason why the initialization failed.
**/
void setFailed(const QString &reason);
/**
* @brief Sets whether the backend provides VSync.
*
* Should be called by the concrete subclass once it is determined whether VSync is supported.
* If the subclass does not call this method, the backend defaults to @c false.
* @param enabled @c true if VSync support available, @c false otherwise.
**/
void setHasWaitSync(bool enabled) {
m_waitSync = enabled;
}
/**
* @brief Sets whether the OpenGL context is direct.
*
* Should be called by the concrete subclass once it is determined whether the OpenGL context is
* direct or indirect.
* If the subclass does not call this method, the backend defaults to @c false.
*
* @param direct @c true if the OpenGL context is direct, @c false if indirect
**/
void setIsDirectRendering(bool direct) {
m_directRendering = direct;
}
/**
* @brief Sets whether the OpenGL context uses double buffering.
*
* Should be called by the concrete subclass once it is determined whether the OpenGL context
* uses double buffering.
* If the subclass does not call this method, the backend defaults to @c false.
*
* @param doubleBuffer @c true if double buffering, @c false otherwise
**/
void setDoubleBuffer(bool doubleBuffer) {
m_doubleBuffer = doubleBuffer;
}
/**
* @return const QRegion& Damage of previously rendered frame
**/
const QRegion &lastDamage() const {
return m_lastDamage;
}
void setLastDamage(const QRegion &damage) {
m_lastDamage = damage;
}
/**
* @return int Rendering mask of previously rendered frame
**/
int lastMask() const {
return m_lastMask;
}
void setLastMask(int mask) {
m_lastMask = mask;
}
/**
* @brief Starts the timer for how long it takes to render the frame.
*
* @see renderTime
**/
void startRenderTimer() {
m_renderTimer.start();
}
private:
/**
* @brief The OverlayWindow used by this Backend.
**/
OverlayWindow *m_overlayWindow;
/**
* @brief Whether VSync is available, defaults to @c false.
**/
bool m_waitSync;
/**
* @brief Whether direct rendering is used, defaults to @c false.
**/
bool m_directRendering;
/**
* @brief Whether double bufferering is available, defaults to @c false.
**/
bool m_doubleBuffer;
/**
* @brief Whether the initialization failed, of course default to @c false.
**/
bool m_failed;
/**
* @brief Damaged region of previously rendered frame.
**/
QRegion m_lastDamage;
/**
* @brief Rendering mask of previously rendered frame.
**/
int m_lastMask;
/**
* @brief Timer to measure how long a frame renders.
**/
QElapsedTimer m_renderTimer;
};
inline bool SceneOpenGL::hasPendingFlush() const
{
return m_backend->hasPendingFlush();
}
} // namespace
#endif

View File

@ -89,6 +89,7 @@ ScreenPaintData SceneXrender::screen_paint;
SceneXrender::SceneXrender(Workspace* ws)
: Scene(ws)
, front(None)
, m_overlayWindow(new OverlayWindow())
, init_ok(false)
{
if (!Extensions::renderAvailable()) {
@ -115,6 +116,7 @@ SceneXrender::~SceneXrender()
m_overlayWindow->destroy();
foreach (Window * w, windows)
delete w;
delete m_overlayWindow;
}
void SceneXrender::initXRender(bool createOverlay)

View File

@ -49,6 +49,9 @@ public:
virtual void windowDeleted(Deleted*);
virtual void screenGeometryChanged(const QSize &size);
Picture bufferPicture();
virtual OverlayWindow *overlayWindow() {
return m_overlayWindow;
}
protected:
virtual void paintBackground(QRegion region);
virtual void paintGenericScreen(int mask, ScreenPaintData data);
@ -66,6 +69,7 @@ private:
static ScreenPaintData screen_paint;
class Window;
QHash< Toplevel*, Window* > windows;
OverlayWindow* m_overlayWindow;
bool init_ok;
};