diff --git a/libkwineffects/kwingltexture.cpp b/libkwineffects/kwingltexture.cpp index 7b67778789..6fa1b3393e 100644 --- a/libkwineffects/kwingltexture.cpp +++ b/libkwineffects/kwingltexture.cpp @@ -26,6 +26,8 @@ along with this program. If not, see . #include "kwinglutils_funcs.h" #include "kwinglutils.h" +#include "kwingltexture_p.h" + #include #include #include @@ -40,52 +42,62 @@ namespace KWin // GLTexture //**************************************** -bool GLTexture::sNPOTTextureSupported = false; -bool GLTexture::sFramebufferObjectSupported = false; -bool GLTexture::sSaturationSupported = false; +bool GLTexturePrivate::sNPOTTextureSupported = false; +bool GLTexturePrivate::sFramebufferObjectSupported = false; +bool GLTexturePrivate::sSaturationSupported = false; GLTexture::GLTexture() + : d_ptr(new GLTexturePrivate()) +{ +} + +GLTexture::GLTexture(GLTexturePrivate& dd) + : d_ptr(&dd) +{ +} + +GLTexture::GLTexture(const GLTexture& tex) + : d_ptr(tex.d_ptr) { - init(); } GLTexture::GLTexture(const QImage& image, GLenum target) + : d_ptr(new GLTexturePrivate()) { - init(); load(image, target); } GLTexture::GLTexture(const QPixmap& pixmap, GLenum target) + : d_ptr(new GLTexturePrivate()) { - init(); load(pixmap, target); } GLTexture::GLTexture(const QString& fileName) + : d_ptr(new GLTexturePrivate()) { - init(); load(fileName); } GLTexture::GLTexture(int width, int height) + : d_ptr(new GLTexturePrivate()) { - init(); - + Q_D(GLTexture); if (NPOTTextureSupported() || (isPowerOfTwo(width) && isPowerOfTwo(height))) { - mTarget = GL_TEXTURE_2D; - mScale.setWidth(1.0 / width); - mScale.setHeight(1.0 / height); - mSize = QSize(width, height); - can_use_mipmaps = true; + d->m_target = GL_TEXTURE_2D; + d->m_scale.setWidth(1.0 / width); + d->m_scale.setHeight(1.0 / height); + d->m_size = QSize(width, height); + d->m_canUseMipmaps = true; - glGenTextures(1, &mTexture); + glGenTextures(1, &d->m_texture); bind(); #ifdef KWIN_HAVE_OPENGLES // format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available // see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml - glTexImage2D(mTarget, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(d->m_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); #else - glTexImage2D(mTarget, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(d->m_target, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); #endif unbind(); } @@ -93,26 +105,38 @@ GLTexture::GLTexture(int width, int height) GLTexture::~GLTexture() { - delete m_vbo; - discard(); - assert(mUnnormalizeActive == 0); - assert(mNormalizeActive == 0); } -void GLTexture::init() +GLTexture& GLTexture::operator = (const GLTexture& tex) { - mTexture = None; - mTarget = 0; - mFilter = 0; - y_inverted = false; - can_use_mipmaps = false; - has_valid_mipmaps = false; - mUnnormalizeActive = 0; - mNormalizeActive = 0; + d_ptr = tex.d_ptr; + return *this; +} + +GLTexturePrivate::GLTexturePrivate() +{ + m_texture = 0; + m_target = 0; + m_filter = 0; + m_yInverted = false; + m_canUseMipmaps = false; + m_hasValidMipmaps = false; + m_unnormalizeActive = 0; + m_normalizeActive = 0; m_vbo = 0; } -void GLTexture::initStatic() +GLTexturePrivate::~GLTexturePrivate() +{ + if (m_vbo != 0) { + delete m_vbo; + } + if (m_texture != 0) { + glDeleteTextures(1, &m_texture); + } +} + +void GLTexturePrivate::initStatic() { #ifdef KWIN_HAVE_OPENGLES sNPOTTextureSupported = true; @@ -129,22 +153,28 @@ void GLTexture::initStatic() bool GLTexture::isNull() const { - return mTexture == None; + Q_D(const GLTexture); + return None == d->m_texture; } QSize GLTexture::size() const { - return mSize; + Q_D(const GLTexture); + return d->m_size; } bool GLTexture::load(const QImage& image, GLenum target) { + // decrease the reference counter for the old texture + d_ptr = new GLTexturePrivate(); + + Q_D(GLTexture); if (image.isNull()) return false; QImage img = image; - mTarget = target; + d->m_target = target; #ifndef KWIN_HAVE_OPENGLES - if (mTarget != GL_TEXTURE_RECTANGLE_ARB) { + if (d->m_target != GL_TEXTURE_RECTANGLE_ARB) { #endif if (!NPOTTextureSupported() && (!isPowerOfTwo(image.width()) || !isPowerOfTwo(image.height()))) { @@ -152,33 +182,33 @@ bool GLTexture::load(const QImage& image, GLenum target) img = img.scaled(nearestPowerOfTwo(image.width()), nearestPowerOfTwo(image.height())); } - mScale.setWidth(1.0 / img.width()); - mScale.setHeight(1.0 / img.height()); - can_use_mipmaps = true; + d->m_scale.setWidth(1.0 / img.width()); + d->m_scale.setHeight(1.0 / img.height()); + d->m_canUseMipmaps = true; #ifndef KWIN_HAVE_OPENGLES } else { - mScale.setWidth(1.0); - mScale.setHeight(1.0); - can_use_mipmaps = false; + d->m_scale.setWidth(1.0); + d->m_scale.setHeight(1.0); + d->m_canUseMipmaps = false; } #endif setFilter(GL_LINEAR); - mSize = img.size(); - y_inverted = true; + d->m_size = img.size(); + d->m_yInverted = true; - img = convertToGLFormat(img); + img = d->convertToGLFormat(img); setDirty(); if (isNull()) { - glGenTextures(1, &mTexture); + glGenTextures(1, &d->m_texture); } bind(); #ifdef KWIN_HAVE_OPENGLES // format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available // see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml - glTexImage2D(mTarget, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + glTexImage2D(d->m_target, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); #else - glTexImage2D(mTarget, 0, GL_RGBA8, img.width(), img.height(), 0, + glTexImage2D(d->m_target, 0, GL_RGBA8, img.width(), img.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, img.bits()); #endif unbind(); @@ -201,37 +231,47 @@ bool GLTexture::load(const QString& fileName) void GLTexture::discard() { - setDirty(); - if (mTexture != None) - glDeleteTextures(1, &mTexture); - mTexture = None; + d_ptr = new GLTexturePrivate(); +} + +void GLTexturePrivate::bind() +{ +#ifndef KWIN_HAVE_OPENGLES + glEnable(m_target); +#endif + glBindTexture(m_target, m_texture); + enableFilter(); } void GLTexture::bind() { + Q_D(GLTexture); + d->bind(); +} + +void GLTexturePrivate::unbind() +{ + glBindTexture(m_target, 0); #ifndef KWIN_HAVE_OPENGLES - glEnable(mTarget); + glDisable(m_target); #endif - glBindTexture(mTarget, mTexture); - enableFilter(); } void GLTexture::unbind() { - glBindTexture(mTarget, 0); -#ifndef KWIN_HAVE_OPENGLES - glDisable(mTarget); -#endif + Q_D(GLTexture); + d->unbind(); } void GLTexture::render(QRegion region, const QRect& rect) { - if (rect.size() != m_cachedSize) { - m_cachedSize = rect.size(); + Q_D(GLTexture); + if (rect.size() != d->m_cachedSize) { + d->m_cachedSize = rect.size(); QRect r(rect); r.moveTo(0, 0); - if (!m_vbo) { - m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static); + if (!d->m_vbo) { + d->m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static); } const float verts[ 4 * 2 ] = { // NOTICE: r.x/y could be replaced by "0", but that would make it unreadable... @@ -241,12 +281,12 @@ void GLTexture::render(QRegion region, const QRect& rect) r.x() + rect.width(), r.y() + rect.height() }; const float texcoords[ 4 * 2 ] = { - 0.0f, y_inverted ? 0.0f : 1.0f, // y needs to be swapped (normalized coords) - 0.0f, y_inverted ? 1.0f : 0.0f, - 1.0f, y_inverted ? 0.0f : 1.0f, - 1.0f, y_inverted ? 1.0f : 0.0f + 0.0f, d->m_yInverted ? 0.0f : 1.0f, // y needs to be swapped (normalized coords) + 0.0f, d->m_yInverted ? 1.0f : 0.0f, + 1.0f, d->m_yInverted ? 0.0f : 1.0f, + 1.0f, d->m_yInverted ? 1.0f : 0.0f }; - m_vbo->setData(4, 2, verts, texcoords); + d->m_vbo->setData(4, 2, verts, texcoords); } QMatrix4x4 translation; translation.translate(rect.x(), rect.y()); @@ -257,7 +297,7 @@ void GLTexture::render(QRegion region, const QRect& rect) } else { pushMatrix(translation); } - m_vbo->render(region, GL_TRIANGLE_STRIP); + d->m_vbo->render(region, GL_TRIANGLE_STRIP); if (ShaderManager::instance()->isShaderBound()) { GLShader *shader = ShaderManager::instance()->getBoundShader(); shader->setUniform(GLShader::WindowTransformation, QMatrix4x4()); @@ -268,85 +308,94 @@ void GLTexture::render(QRegion region, const QRect& rect) GLuint GLTexture::texture() const { - return mTexture; + Q_D(const GLTexture); + return d->m_texture; } GLenum GLTexture::target() const { - return mTarget; + Q_D(const GLTexture); + return d->m_target; } GLenum GLTexture::filter() const { - return mFilter; + Q_D(const GLTexture); + return d->m_filter; } bool GLTexture::isDirty() const { - return has_valid_mipmaps; + Q_D(const GLTexture); + return d->m_hasValidMipmaps; } void GLTexture::setTexture(GLuint texture) { + Q_D(GLTexture); discard(); - mTexture = texture; + d->m_texture = texture; } void GLTexture::setTarget(GLenum target) { - mTarget = target; + Q_D(GLTexture); + d->m_target = target; } void GLTexture::setFilter(GLenum filter) { - mFilter = filter; + Q_D(GLTexture); + d->m_filter = filter; } void GLTexture::setWrapMode(GLenum mode) { + Q_D(GLTexture); bind(); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, mode); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, mode); + glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, mode); + glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, mode); unbind(); } void GLTexture::setDirty() { - has_valid_mipmaps = false; + Q_D(GLTexture); + d->m_hasValidMipmaps = false; } -void GLTexture::enableFilter() +void GLTexturePrivate::enableFilter() { - if (mFilter == GL_LINEAR_MIPMAP_LINEAR) { + if (m_filter == GL_LINEAR_MIPMAP_LINEAR) { // trilinear filtering requested, but is it possible? - if (NPOTTextureSupported() - && framebufferObjectSupported() - && can_use_mipmaps) { - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (!has_valid_mipmaps) { - glGenerateMipmap(mTarget); - has_valid_mipmaps = true; + 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 - setFilter(GL_LINEAR); - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + m_filter = GL_LINEAR; + glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - } else if (mFilter == GL_LINEAR) { - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(mTarget, 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 - setFilter(GL_NEAREST); - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + m_filter = GL_NEAREST; + glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } -QImage GLTexture::convertToGLFormat(const QImage& img) const +QImage GLTexturePrivate::convertToGLFormat(const QImage& img) const { // Copied from Qt's QGLWidget::convertToGLFormat() QImage res; @@ -410,7 +459,52 @@ QImage GLTexture::convertToGLFormat(const QImage& img) const bool GLTexture::isYInverted() const { - return y_inverted; + Q_D(const GLTexture); + return d->m_yInverted; +} + +void GLTexture::setYInverted(bool inverted) +{ + Q_D(GLTexture); + d->m_yInverted = inverted; +} + +int GLTexture::width() const +{ + Q_D(const GLTexture); + return d->m_size.width(); +} + +int GLTexture::height() const +{ + Q_D(const GLTexture); + return d->m_size.height(); +} + +bool GLTexture::NPOTTextureSupported() +{ + return GLTexturePrivate::sNPOTTextureSupported; +} + +bool GLTexture::framebufferObjectSupported() +{ + return GLTexturePrivate::sFramebufferObjectSupported; +} + +bool GLTexture::saturationSupported() +{ + return GLTexturePrivate::sSaturationSupported; +} + +void GLTexture::release() +{ + Q_D(GLTexture); + d->release(); +} + +void GLTexturePrivate::release() +{ + // nothing to do because we are not bound to any specific data } } // namespace KWin diff --git a/libkwineffects/kwingltexture.h b/libkwineffects/kwingltexture.h index 9a8d4168b6..b114838a51 100644 --- a/libkwineffects/kwingltexture.h +++ b/libkwineffects/kwingltexture.h @@ -25,7 +25,8 @@ along with this program. If not, see . #include "kwinglobals.h" #include -#include +#include +#include class QImage; class QPixmap; @@ -37,37 +38,45 @@ namespace KWin { class GLVertexBuffer; +class GLTexturePrivate; class KWIN_EXPORT GLTexture - : public QSharedData { public: GLTexture(); + GLTexture(const GLTexture& tex); explicit GLTexture(const QImage& image, GLenum target = GL_TEXTURE_2D); explicit GLTexture(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D); GLTexture(const QString& fileName); GLTexture(int width, int height); virtual ~GLTexture(); + GLTexture & operator = (const GLTexture& tex); + bool isNull() const; QSize size() const; - int width() const { - return mSize.width(); /// @since 4.5 - } - int height() const { - return mSize.height(); /// @since 4.5 - } + int width() const; + int height() const; /** * @since 4.7 **/ bool isYInverted() const; + /** + * @since 4.8 + **/ + void setYInverted(bool inverted); virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D); virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D); virtual bool load(const QString& fileName); virtual void discard(); - virtual void bind(); - virtual void unbind(); + void bind(); + void unbind(); + /** + * Release the data which is bound to the texture. + * @since 4.8 + **/ + void release(); void render(QRegion region, const QRect& rect); GLuint texture() const; @@ -80,41 +89,19 @@ public: void setWrapMode(GLenum mode); virtual void setDirty(); - static void initStatic(); - static bool NPOTTextureSupported() { - return sNPOTTextureSupported; - } - static bool framebufferObjectSupported() { - return sFramebufferObjectSupported; - } - static bool saturationSupported() { - return sSaturationSupported; - } + static bool NPOTTextureSupported(); + static bool framebufferObjectSupported(); + static bool saturationSupported(); protected: void enableFilter(); QImage convertToGLFormat(const QImage& img) const; - GLuint mTexture; - GLenum mTarget; - GLenum mFilter; - QSize mSize; - QSizeF mScale; // to un-normalize GL_TEXTURE_2D - bool y_inverted; // texture has y inverted - bool can_use_mipmaps; - bool has_valid_mipmaps; + QExplicitlySharedDataPointer d_ptr; + GLTexture(GLTexturePrivate& dd); private: - void init(); - int mUnnormalizeActive; // 0 - no, otherwise refcount - int mNormalizeActive; // 0 - no, otherwise refcount - GLVertexBuffer* m_vbo; - QSize m_cachedSize; - - static bool sNPOTTextureSupported; - static bool sFramebufferObjectSupported; - static bool sSaturationSupported; - Q_DISABLE_COPY(GLTexture) + Q_DECLARE_PRIVATE(GLTexture); }; } // namespace diff --git a/libkwineffects/kwingltexture_p.h b/libkwineffects/kwingltexture_p.h new file mode 100644 index 0000000000..be879e3073 --- /dev/null +++ b/libkwineffects/kwingltexture_p.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2006-2007 Rivo Laks +Copyright (C) 2010, 2011 Martin Gräßlin +Copyright (C) 2011 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 +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_GLTEXTURE_P_H +#define KWIN_GLTEXTURE_P_H + +#include "kwinconfig.h" // KWIN_HAVE_OPENGL +#include "kwinglobals.h" + +#include +#include + +namespace KWin +{ + +class KWIN_EXPORT GLTexturePrivate + : public QSharedData +{ +public: + GLTexturePrivate(); + virtual ~GLTexturePrivate(); + + virtual void bind(); + virtual void unbind(); + virtual void release(); + + void enableFilter(); + QImage convertToGLFormat(const QImage& img) const; + + GLuint m_texture; + GLenum m_target; + GLenum m_filter; + 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; + + int m_unnormalizeActive; // 0 - no, otherwise refcount + int m_normalizeActive; // 0 - no, otherwise refcount + GLVertexBuffer* m_vbo; + QSize m_cachedSize; + + static void initStatic(); + + static bool sNPOTTextureSupported; + static bool sFramebufferObjectSupported; + static bool sSaturationSupported; +private: + Q_DISABLE_COPY(GLTexturePrivate) +}; + +} // namespace + +#endif \ No newline at end of file diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp index b9bba5b4e9..19b4ebb011 100644 --- a/libkwineffects/kwinglutils.cpp +++ b/libkwineffects/kwinglutils.cpp @@ -21,6 +21,9 @@ along with this program. If not, see . #include "kwinglutils.h" +// need to call GLTexturePrivate::initStatic() +#include "kwingltexture_p.h" + #include "kwinglobals.h" #include "kwineffects.h" #include "kwinglplatform.h" @@ -113,7 +116,7 @@ void initGL() // handle OpenGL extensions functions glResolveFunctions(); - GLTexture::initStatic(); + GLTexturePrivate::initStatic(); GLRenderTarget::initStatic(); GLVertexBuffer::initStatic(); } diff --git a/scene_opengl.cpp b/scene_opengl.cpp index e0fc5f7b4a..cf3eb654d0 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -268,55 +268,43 @@ void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t) // SceneOpenGL::Texture //**************************************** -SceneOpenGL::Texture::Texture() : GLTexture() +SceneOpenGL::Texture::Texture() : GLTexture(*new TexturePrivate()) { - init(); } -SceneOpenGL::Texture::Texture(const Pixmap& pix, const QSize& size, int depth) : GLTexture() +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()) { - init(); load(pix, size, depth); } SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target) - : GLTexture() + : GLTexture(*new TexturePrivate()) { - init(); load(pix, target); } SceneOpenGL::Texture::~Texture() { - discard(); } -void SceneOpenGL::Texture::createTexture() +SceneOpenGL::Texture& SceneOpenGL::Texture::operator = (const SceneOpenGL::Texture& tex) { - glGenTextures(1, &mTexture); + d_ptr = tex.d_ptr; + return *this; } void SceneOpenGL::Texture::discard() { - if (mTexture != None) - release(); - GLTexture::discard(); -} - -QRegion SceneOpenGL::Texture::optimizeBindDamage(const QRegion& reg, int limit) -{ - if (reg.rects().count() <= 1) - return reg; - // try to reduce the number of rects, as especially with SHM mode every rect - // causes X roundtrip, even for very small areas - so, when the size difference - // between all the areas and the bounding rectangle is small, simply use - // only the bounding rectangle - int size = 0; - foreach (const QRect & r, reg.rects()) - size += r.width() * r.height(); - if (reg.boundingRect().width() * reg.boundingRect().height() - size < limit) - return reg.boundingRect(); - return reg; + d_ptr = new TexturePrivate(); } bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, @@ -337,6 +325,7 @@ bool SceneOpenGL::Texture::load(const QImage& image, GLenum target) bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target) { + Q_D(Texture); if (pixmap.isNull()) return false; @@ -345,8 +334,6 @@ bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target) return GLTexture::load(pixmap.toImage(), target); } - y_inverted = true; - // use the X11 pixmap provided by Qt return load(pixmap.handle(), pixmap.size(), pixmap.depth()); } @@ -374,9 +361,16 @@ SceneOpenGL::Window::~Window() bool SceneOpenGL::Window::bindTexture() { #ifndef KWIN_HAVE_OPENGLES - if (texture.texture() != None && toplevel->damage().isEmpty()) { - // texture doesn't need updating, just bind it - glBindTexture(texture.target(), texture.texture()); + if (!texture.isNull()) { + if (toplevel->damage().isEmpty()) { + // texture doesn't need updating, just bind it + glBindTexture(texture.target(), texture.texture()); + } else { + // bind() updates the texture automatically e.g. in case the glx pixmap binding + // is strict + texture.bind(); + toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); + } return true; } #endif @@ -384,8 +378,10 @@ bool SceneOpenGL::Window::bindTexture() Pixmap pix = toplevel->windowPixmap(); if (pix == None) return false; + bool success = texture.load(pix, toplevel->size(), toplevel->depth(), toplevel->damage()); + if (success) toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize())); else @@ -417,7 +413,7 @@ void SceneOpenGL::Window::checkTextureSize() // when the window's composite pixmap is discarded, undo binding it to the texture void SceneOpenGL::Window::pixmapDiscarded() { - texture.release(); + texture.discard(); } QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const @@ -640,7 +636,7 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType } if (decorationTexture->texture() != None && !updateDeco) { // texture doesn't need updating, just bind it - glBindTexture(decorationTexture->target(), decorationTexture->texture()); + decorationTexture->bind(); } else if (!decoration->isNull()) { bool success = decorationTexture->load(*decoration); if (!success) { diff --git a/scene_opengl.h b/scene_opengl.h index 52107ea8f9..600ae2e6c2 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -26,6 +26,7 @@ along with this program. If not, see . #include "shadow.h" #include "kwinglutils.h" +#include "kwingltexture_p.h" #ifdef HAVE_XSHM #include @@ -41,6 +42,7 @@ class SceneOpenGL public: class EffectFrame; class Texture; + class TexturePrivate; class Window; SceneOpenGL(Workspace* ws); virtual ~SceneOpenGL(); @@ -100,40 +102,51 @@ private: bool debug; }; +class SceneOpenGL::TexturePrivate + : public GLTexturePrivate +{ +public: + TexturePrivate(); + virtual ~TexturePrivate(); + + virtual void bind(); + virtual void unbind(); + virtual void release(); + +#ifndef KWIN_HAVE_OPENGLES + GLXPixmap m_glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode +#endif +private: + Q_DISABLE_COPY(TexturePrivate) +}; + class SceneOpenGL::Texture : public GLTexture { public: Texture(); + Texture(const Texture& tex); Texture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D); virtual ~Texture(); + Texture & operator = (const Texture& tex); + using GLTexture::load; virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D); virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D); virtual void discard(); - virtual void release(); // undo the tfp_mode binding - virtual void bind(); - virtual void unbind(); - void setYInverted(bool inverted) { - y_inverted = inverted; - } protected: Texture(const Pixmap& pix, const QSize& size, int depth); void findTarget(); - QRegion optimizeBindDamage(const QRegion& reg, int limit); - void createTexture(); virtual bool load(const Pixmap& pix, const QSize& size, int depth, QRegion region); virtual bool load(const Pixmap& pix, const QSize& size, int depth); -private: - void init(); + Texture(TexturePrivate& dd); -#ifndef KWIN_HAVE_OPENGLES - GLXPixmap glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode -#endif +private: + Q_DECLARE_PRIVATE(Texture); friend class SceneOpenGL::Window; }; diff --git a/scene_opengl_egl.cpp b/scene_opengl_egl.cpp index 8b1b51225c..4e232c2691 100644 --- a/scene_opengl_egl.cpp +++ b/scene_opengl_egl.cpp @@ -210,65 +210,71 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage) // SceneOpenGL::Texture //**************************************** -void SceneOpenGL::Texture::init() +SceneOpenGL::TexturePrivate::TexturePrivate() { - findTarget(); + m_target = GL_TEXTURE_2D; } -void SceneOpenGL::Texture::release() +SceneOpenGL::TexturePrivate::~TexturePrivate() { - mTexture = None; } void SceneOpenGL::Texture::findTarget() { - mTarget = GL_TEXTURE_2D; + Q_D(Texture); + d->m_target = GL_TEXTURE_2D; +} + +void SceneOpenGL::TexturePrivate::release() +{ } bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, int depth, QRegion region) { + // decrease the reference counter for the old texture + d_ptr = new TexturePrivate(); + + Q_D(Texture); Q_UNUSED(depth) Q_UNUSED(region) if (pix == None) return false; - if (mTexture == None) { - createTexture(); - 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 - }; - EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, - (EGLClientBuffer)pix, attribs); + glGenTextures(1, &d->m_texture); + 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 + }; + EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)pix, attribs); - if (EGL_NO_IMAGE_KHR == image) { - kDebug(1212) << "failed to create egl image"; - unbind(); - return false; - } - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); - eglDestroyImageKHR(dpy, image); + if (EGL_NO_IMAGE_KHR == image) { + kDebug(1212) << "failed to create egl image"; unbind(); - checkGLError("load texture"); - setYInverted(true); - mSize = size; + return false; } + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); + eglDestroyImageKHR(dpy, image); + unbind(); + checkGLError("load texture"); + setYInverted(true); + d->m_size = size; return true; } -void SceneOpenGL::Texture::bind() +void SceneOpenGL::TexturePrivate::bind() { - GLTexture::bind(); + GLTexturePrivate::bind(); } -void SceneOpenGL::Texture::unbind() +void SceneOpenGL::TexturePrivate::unbind() { - GLTexture::unbind(); + GLTexturePrivate::unbind(); } diff --git a/scene_opengl_glx.cpp b/scene_opengl_glx.cpp index 9ceeb761db..06a7b0a575 100644 --- a/scene_opengl_glx.cpp +++ b/scene_opengl_glx.cpp @@ -55,6 +55,7 @@ SceneOpenGL::SceneOpenGL(Workspace* ws) return; // error // Initialize OpenGL initGL(); + GLPlatform *glPlatform = GLPlatform::instance(); if (glPlatform->isSoftwareEmulation()) { kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender."; @@ -544,33 +545,38 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage) // SceneOpenGL::Texture //**************************************** -void SceneOpenGL::Texture::init() +SceneOpenGL::TexturePrivate::TexturePrivate() { - glxpixmap = None; + m_glxpixmap = None; } -void SceneOpenGL::Texture::release() +SceneOpenGL::TexturePrivate::~TexturePrivate() { - if (glxpixmap != None) { + release(); +} + +void SceneOpenGL::TexturePrivate::release() +{ + if (m_glxpixmap != None) { if (!options->glStrictBinding) { - glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT); + glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); } - glXDestroyPixmap(display(), glxpixmap); - glxpixmap = None; + glXDestroyPixmap(display(), m_glxpixmap); } } void SceneOpenGL::Texture::findTarget() { + Q_D(Texture); unsigned int new_target = 0; - if (glXQueryDrawable && glxpixmap != None) - glXQueryDrawable(display(), glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target); + 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(mSize.width()) && isPowerOfTwo(mSize.height()))) { + (isPowerOfTwo(d->m_size.width()) && isPowerOfTwo(d->m_size.height()))) { new_target = GLX_TEXTURE_2D_EXT; } else { new_target = GLX_TEXTURE_RECTANGLE_EXT; @@ -578,14 +584,14 @@ void SceneOpenGL::Texture::findTarget() } switch(new_target) { case GLX_TEXTURE_2D_EXT: - mTarget = GL_TEXTURE_2D; - mScale.setWidth(1.0f / mSize.width()); - mScale.setHeight(1.0f / mSize.height()); + 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: - mTarget = GL_TEXTURE_RECTANGLE_ARB; - mScale.setWidth(1.0f); - mScale.setHeight(1.0f); + d->m_target = GL_TEXTURE_RECTANGLE_ARB; + d->m_scale.setWidth(1.0f); + d->m_scale.setHeight(1.0f); break; default: abort(); @@ -595,6 +601,10 @@ void SceneOpenGL::Texture::findTarget() bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, int depth, QRegion region) { + // decrease the reference counter for the old texture + d_ptr = new TexturePrivate(); + + Q_D(Texture); #ifdef CHECK_GL_ERROR checkGLError("TextureLoad1"); #endif @@ -606,78 +616,65 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size, return false; } - mSize = size; - if (mTexture == None || !region.isEmpty()) { - // new texture, or texture contents changed; mipmaps now invalid - setDirty(); - } + d->m_size = size; + d->m_yInverted = true; + // 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 - if (mTexture == None) - createTexture(); + 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. - if (glxpixmap != None) - glBindTexture(mTarget, mTexture); - else { - int attrs[] = { - GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format, - GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap, - None, None, None - }; - // 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; - } + int attrs[] = { + GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format, + GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap, + 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; } - glxpixmap = glXCreatePixmap(display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs); -#ifdef CHECK_GL_ERROR - checkGLError("TextureLoadTFP1"); -#endif - findTarget(); - y_inverted = fbcdrawableinfo[ depth ].y_inverted ? true : false; - can_use_mipmaps = fbcdrawableinfo[ depth ].mipmap ? true : false; - glBindTexture(mTarget, mTexture); -#ifdef CHECK_GL_ERROR - checkGLError("TextureLoadTFP2"); -#endif - if (!options->glStrictBinding) - glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL); } - if (options->glStrictBinding) - // Mark the texture as damaged so it will be updated on the next call to bind() - glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL); + 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 ? true : false; + 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 return true; } -void SceneOpenGL::Texture::bind() +void SceneOpenGL::TexturePrivate::bind() { - glEnable(mTarget); - glBindTexture(mTarget, mTexture); - // if one of the GLTexture::load functions is called, the glxpixmap doesn't - // have to exist - if (options->glStrictBinding && glxpixmap) { - glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT); - glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL); - setDirty(); // Mipmaps have to be regenerated after updating the texture + 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)) { @@ -686,17 +683,16 @@ void SceneOpenGL::Texture::bind() } } -void SceneOpenGL::Texture::unbind() +void SceneOpenGL::TexturePrivate::unbind() { if (hasGLVersion(1, 4, 0)) { glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f); } - // if one of the GLTexture::load functions is called, the glxpixmap doesn't - // have to exist - if (options->glStrictBinding && glxpixmap) { - glBindTexture(mTarget, mTexture); - glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT); + if (options->glStrictBinding && m_glxpixmap) { + glBindTexture(m_target, m_texture); + glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT); } - GLTexture::unbind(); + GLTexturePrivate::unbind(); } +