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
This commit is contained in:
Philipp Knechtges 2012-01-07 12:12:29 +01:00
parent 5fde665578
commit 87bcabdf99
6 changed files with 83 additions and 63 deletions

View file

@ -4,6 +4,7 @@
Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>
Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
Copyright (C) 2012 Philipp Knechtges <philipp-dev@knechtges.com>
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

View file

@ -94,7 +94,6 @@ public:
static bool saturationSupported();
protected:
void enableFilter();
QImage convertToGLFormat(const QImage& img) const;
QExplicitlySharedDataPointer<GLTexturePrivate> d_ptr;

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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();
}