diff --git a/CMakeLists.txt b/CMakeLists.txt index b65d5ee30f..458c341f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/effects.cpp b/effects.cpp index 0f3084474a..dabb96aeca 100644 --- a/effects.cpp +++ b/effects.cpp @@ -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(Compositor::self()->scene())); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING m_sceneFrame = new SceneXrender::EffectFrame(this); diff --git a/scene_opengl_egl.cpp b/eglonxbackend.cpp similarity index 55% rename from scene_opengl_egl.cpp rename to eglonxbackend.cpp index 06d196a480..8797c8aaa2 100644 --- a/scene_opengl_egl.cpp +++ b/eglonxbackend.cpp @@ -2,7 +2,7 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2010 Martin Gräßlin +Copyright (C) 2010, 2012 Martin Gräßlin 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 . *********************************************************************/ +#ifdef KWIN_HAVE_OPENGLES +#include "eglonxbackend.h" +// kwin +#include "options.h" +#include "overlaywindow.h" +// kwin libs +#include -// This file is included in scene_opengl.cpp -//#include "scene_opengl.h" -#include - -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 diff --git a/eglonxbackend.h b/eglonxbackend.h new file mode 100644 index 0000000000..4b7eb25f50 --- /dev/null +++ b/eglonxbackend.h @@ -0,0 +1,77 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2012 Martin Gräßlin + +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 . +*********************************************************************/ +#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 diff --git a/scene_opengl_glx.cpp b/glxbackend.cpp similarity index 72% rename from scene_opengl_glx.cpp rename to glxbackend.cpp index e52d92881a..cef88d433a 100644 --- a/scene_opengl_glx.cpp +++ b/glxbackend.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak +Copyright (C) 2012 Martin Gräßlin 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 . *********************************************************************/ -// 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 +// KDE +#include +#include -// 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 diff --git a/glxbackend.h b/glxbackend.h new file mode 100644 index 0000000000..6fd8c3032e --- /dev/null +++ b/glxbackend.h @@ -0,0 +1,93 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2012 Martin Gräßlin + +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 . +*********************************************************************/ +#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 diff --git a/scene.cpp b/scene.cpp index 6d847771ba..ca35d7debe 100644 --- a/scene.cpp +++ b/scene.cpp @@ -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); } //**************************************** diff --git a/scene.h b/scene.h index 70f9f67091..d509d4d506 100644 --- a/scene.h +++ b/scene.h @@ -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 lanczos_filter; - OverlayWindow* m_overlayWindow; }; // The base class for windows representations in composite backends diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 0372c039a2..ec66529d3a 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -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 @@ -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() diff --git a/scene_opengl.h b/scene_opengl.h index df65920e1d..23e5aa7dd3 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -30,6 +30,7 @@ along with this program. If not, see . 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 + **/ +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 diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 2b8aec1118..f0ac20b23a 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -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) diff --git a/scene_xrender.h b/scene_xrender.h index f312759bfc..f17259464c 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -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; };