From 87bcabdf994492816a063200e78f362317e45295 Mon Sep 17 00:00:00 2001 From: Philipp Knechtges Date: Sat, 7 Jan 2012 12:12:29 +0100 Subject: [PATCH] kwin: improving the texture update handling This patch changes the behavior of strictly bound textures such that they are only updated if the corresponding window has been damaged. Additionally GLTexture now keeps track of the current filter and wrapmode setting. REVIEW: 103655 --- libkwineffects/kwingltexture.cpp | 101 +++++++++++++++++-------------- libkwineffects/kwingltexture.h | 1 - libkwineffects/kwingltexture_p.h | 7 ++- scene_opengl.h | 1 + scene_opengl_egl.cpp | 16 +++-- scene_opengl_glx.cpp | 20 +++--- 6 files changed, 83 insertions(+), 63 deletions(-) diff --git a/libkwineffects/kwingltexture.cpp b/libkwineffects/kwingltexture.cpp index 4155fb16f3..9e577e87e8 100644 --- a/libkwineffects/kwingltexture.cpp +++ b/libkwineffects/kwingltexture.cpp @@ -4,6 +4,7 @@ Copyright (C) 2006-2007 Rivo Laks Copyright (C) 2010, 2011 Martin Gräßlin +Copyright (C) 2012 Philipp Knechtges 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 @@ -117,13 +118,16 @@ GLTexturePrivate::GLTexturePrivate() { m_texture = 0; m_target = 0; - m_filter = 0; + m_filter = GL_NEAREST_MIPMAP_LINEAR; + m_wrapMode = GL_REPEAT; m_yInverted = false; m_canUseMipmaps = false; - m_hasValidMipmaps = false; + m_markedDirty = false; m_unnormalizeActive = 0; m_normalizeActive = 0; m_vbo = 0; + m_filterChanged = false; + m_wrapModeChanged = false; } GLTexturePrivate::~GLTexturePrivate() @@ -192,13 +196,11 @@ bool GLTexture::load(const QImage& image, GLenum target) d->m_canUseMipmaps = false; } #endif - setFilter(GL_LINEAR); d->m_size = img.size(); d->m_yInverted = true; img = d->convertToGLFormat(img); - setDirty(); if (isNull()) { glGenTextures(1, &d->m_texture); } @@ -212,6 +214,7 @@ bool GLTexture::load(const QImage& image, GLenum target) GL_BGRA, GL_UNSIGNED_BYTE, img.bits()); #endif unbind(); + setFilter(GL_LINEAR); return true; } @@ -240,13 +243,44 @@ void GLTexturePrivate::bind() glEnable(m_target); #endif glBindTexture(m_target, m_texture); - enableFilter(); } void GLTexture::bind() { Q_D(GLTexture); d->bind(); + if (d->m_markedDirty) { + d->onDamage(); + } + if (d->m_filterChanged) { + if (d->m_filter == GL_LINEAR_MIPMAP_LINEAR) { + // trilinear filtering requested, but is it possible? + if (d->sNPOTTextureSupported + && d->sFramebufferObjectSupported + && d->m_canUseMipmaps) { + glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenerateMipmap(d->m_target); + } else { + // can't use trilinear, so use bilinear + d->m_filter = GL_LINEAR; + glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + } else if (d->m_filter == GL_LINEAR) { + glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + // if neither trilinear nor bilinear, default to fast filtering + d->m_filter = GL_NEAREST; + glTexParameteri(d->m_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(d->m_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + } + if (d->m_wrapModeChanged) { + glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, d->m_wrapMode); + glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, d->m_wrapMode); + } } void GLTexturePrivate::unbind() @@ -334,13 +368,13 @@ GLenum GLTexture::filter() const bool GLTexture::isDirty() const { Q_D(const GLTexture); - return d->m_hasValidMipmaps; + return d->m_markedDirty; } void GLTexture::setTexture(GLuint texture) { - Q_D(GLTexture); discard(); + Q_D(GLTexture); d->m_texture = texture; } @@ -353,53 +387,32 @@ void GLTexture::setTarget(GLenum target) void GLTexture::setFilter(GLenum filter) { Q_D(GLTexture); - d->m_filter = filter; + if (filter != d->m_filter) { + d->m_filter = filter; + d->m_filterChanged = true; + } } void GLTexture::setWrapMode(GLenum mode) { Q_D(GLTexture); - bind(); - glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, mode); - glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, mode); - unbind(); + if (mode != d->m_wrapMode) { + d->m_wrapMode = mode; + d->m_wrapModeChanged=true; + } +} + +void GLTexturePrivate::onDamage() +{ + if (m_filter == GL_LINEAR_MIPMAP_LINEAR && !m_filterChanged) { + glGenerateMipmap(m_target); + } } void GLTexture::setDirty() { Q_D(GLTexture); - d->m_hasValidMipmaps = false; -} - - -void GLTexturePrivate::enableFilter() -{ - if (m_filter == GL_LINEAR_MIPMAP_LINEAR) { - // trilinear filtering requested, but is it possible? - if (sNPOTTextureSupported - && sFramebufferObjectSupported - && m_canUseMipmaps) { - glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (!m_hasValidMipmaps) { - glGenerateMipmap(m_target); - m_hasValidMipmaps = true; - } - } else { - // can't use trilinear, so use bilinear - m_filter = GL_LINEAR; - glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - } else if (m_filter == GL_LINEAR) { - glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - // if neither trilinear nor bilinear, default to fast filtering - m_filter = GL_NEAREST; - glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } + d->m_markedDirty = true; } QImage GLTexturePrivate::convertToGLFormat(const QImage& img) const diff --git a/libkwineffects/kwingltexture.h b/libkwineffects/kwingltexture.h index 6795d0dbf1..b913eca24e 100644 --- a/libkwineffects/kwingltexture.h +++ b/libkwineffects/kwingltexture.h @@ -94,7 +94,6 @@ public: static bool saturationSupported(); protected: - void enableFilter(); QImage convertToGLFormat(const QImage& img) const; QExplicitlySharedDataPointer d_ptr; diff --git a/libkwineffects/kwingltexture_p.h b/libkwineffects/kwingltexture_p.h index 7e04305a92..27c14db550 100644 --- a/libkwineffects/kwingltexture_p.h +++ b/libkwineffects/kwingltexture_p.h @@ -42,18 +42,21 @@ public: virtual void bind(); virtual void unbind(); virtual void release(); + virtual void onDamage(); - void enableFilter(); QImage convertToGLFormat(const QImage& img) const; GLuint m_texture; GLenum m_target; GLenum m_filter; + GLenum m_wrapMode; QSize m_size; QSizeF m_scale; // to un-normalize GL_TEXTURE_2D bool m_yInverted; // texture has y inverted bool m_canUseMipmaps; - bool m_hasValidMipmaps; + bool m_markedDirty; + bool m_filterChanged; + bool m_wrapModeChanged; int m_unnormalizeActive; // 0 - no, otherwise refcount int m_normalizeActive; // 0 - no, otherwise refcount diff --git a/scene_opengl.h b/scene_opengl.h index 5bc9d5e936..957b6b73cd 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -117,6 +117,7 @@ public: virtual void bind(); virtual void unbind(); virtual void release(); + virtual void onDamage(); #ifndef KWIN_HAVE_OPENGLES GLXPixmap m_glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode diff --git a/scene_opengl_egl.cpp b/scene_opengl_egl.cpp index 90841338f5..6855133880 100644 --- a/scene_opengl_egl.cpp +++ b/scene_opengl_egl.cpp @@ -266,11 +266,9 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, return false; glGenTextures(1, &d->m_texture); + setWrapMode(GL_CLAMP_TO_EDGE); + setFilter(GL_LINEAR); bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); const EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE @@ -281,6 +279,7 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, if (EGL_NO_IMAGE_KHR == d->m_image) { kDebug(1212) << "failed to create egl image"; unbind(); + discard(); return false; } glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)d->m_image); @@ -291,15 +290,20 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, return true; } -void SceneOpenGL::TexturePrivate::bind() +void SceneOpenGL::TexturePrivate::onDamage() { - GLTexturePrivate::bind(); if (options->glStrictBinding) { // This is just implemented to be consistent with // the example in mesa/demos/src/egl/opengles1/texture_from_pixmap.c eglWaitNative(EGL_CORE_NATIVE_ENGINE); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) m_image); } + GLTexturePrivate::onDamage(); +} + +void SceneOpenGL::TexturePrivate::bind() +{ + GLTexturePrivate::bind(); } void SceneOpenGL::TexturePrivate::unbind() diff --git a/scene_opengl_glx.cpp b/scene_opengl_glx.cpp index 88c3139cf3..5290edeaee 100644 --- a/scene_opengl_glx.cpp +++ b/scene_opengl_glx.cpp @@ -721,18 +721,22 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, #ifdef CHECK_GL_ERROR checkGLError("TextureLoad0"); #endif + unbind(); return true; } +void SceneOpenGL::TexturePrivate::onDamage() +{ + if (options->glStrictBinding && m_glxpixmap) { + glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); + glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL); + } + GLTexturePrivate::onDamage(); +} + void SceneOpenGL::TexturePrivate::bind() { GLTexturePrivate::bind(); - if (options->glStrictBinding && m_glxpixmap) { - glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); - glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL); - m_hasValidMipmaps = false; // Mipmaps have to be regenerated after updating the texture - } - enableFilter(); if (hasGLVersion(1, 4, 0)) { // Lod bias makes the trilinear-filtered texture look a bit sharper glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f); @@ -744,10 +748,6 @@ void SceneOpenGL::TexturePrivate::unbind() if (hasGLVersion(1, 4, 0)) { glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f); } - if (options->glStrictBinding && m_glxpixmap) { - glBindTexture(m_target, m_texture); - glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); - } GLTexturePrivate::unbind(); }