kwin: Implement "use by value" and implicit sharing for GLTexture.
Additionally: - hide the GLTexture implementation using dpointers - drop the unused function SceneOpenGL::Texture::optimizeBindDamage() - Texture::load now loads a new texture and does not update the existing one REVIEW: 101999
This commit is contained in:
parent
9c2d6f8fdc
commit
4f50a8df3c
8 changed files with 462 additions and 293 deletions
|
@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "kwinglutils_funcs.h"
|
#include "kwinglutils_funcs.h"
|
||||||
#include "kwinglutils.h"
|
#include "kwinglutils.h"
|
||||||
|
|
||||||
|
#include "kwingltexture_p.h"
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QVector2D>
|
#include <QVector2D>
|
||||||
|
@ -40,52 +42,62 @@ namespace KWin
|
||||||
// GLTexture
|
// GLTexture
|
||||||
//****************************************
|
//****************************************
|
||||||
|
|
||||||
bool GLTexture::sNPOTTextureSupported = false;
|
bool GLTexturePrivate::sNPOTTextureSupported = false;
|
||||||
bool GLTexture::sFramebufferObjectSupported = false;
|
bool GLTexturePrivate::sFramebufferObjectSupported = false;
|
||||||
bool GLTexture::sSaturationSupported = false;
|
bool GLTexturePrivate::sSaturationSupported = false;
|
||||||
|
|
||||||
GLTexture::GLTexture()
|
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)
|
GLTexture::GLTexture(const QImage& image, GLenum target)
|
||||||
|
: d_ptr(new GLTexturePrivate())
|
||||||
{
|
{
|
||||||
init();
|
|
||||||
load(image, target);
|
load(image, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture::GLTexture(const QPixmap& pixmap, GLenum target)
|
GLTexture::GLTexture(const QPixmap& pixmap, GLenum target)
|
||||||
|
: d_ptr(new GLTexturePrivate())
|
||||||
{
|
{
|
||||||
init();
|
|
||||||
load(pixmap, target);
|
load(pixmap, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture::GLTexture(const QString& fileName)
|
GLTexture::GLTexture(const QString& fileName)
|
||||||
|
: d_ptr(new GLTexturePrivate())
|
||||||
{
|
{
|
||||||
init();
|
|
||||||
load(fileName);
|
load(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture::GLTexture(int width, int height)
|
GLTexture::GLTexture(int width, int height)
|
||||||
|
: d_ptr(new GLTexturePrivate())
|
||||||
{
|
{
|
||||||
init();
|
Q_D(GLTexture);
|
||||||
|
|
||||||
if (NPOTTextureSupported() || (isPowerOfTwo(width) && isPowerOfTwo(height))) {
|
if (NPOTTextureSupported() || (isPowerOfTwo(width) && isPowerOfTwo(height))) {
|
||||||
mTarget = GL_TEXTURE_2D;
|
d->m_target = GL_TEXTURE_2D;
|
||||||
mScale.setWidth(1.0 / width);
|
d->m_scale.setWidth(1.0 / width);
|
||||||
mScale.setHeight(1.0 / height);
|
d->m_scale.setHeight(1.0 / height);
|
||||||
mSize = QSize(width, height);
|
d->m_size = QSize(width, height);
|
||||||
can_use_mipmaps = true;
|
d->m_canUseMipmaps = true;
|
||||||
|
|
||||||
glGenTextures(1, &mTexture);
|
glGenTextures(1, &d->m_texture);
|
||||||
bind();
|
bind();
|
||||||
#ifdef KWIN_HAVE_OPENGLES
|
#ifdef KWIN_HAVE_OPENGLES
|
||||||
// format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available
|
// 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
|
// 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
|
#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
|
#endif
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
|
@ -93,26 +105,38 @@ GLTexture::GLTexture(int width, int height)
|
||||||
|
|
||||||
GLTexture::~GLTexture()
|
GLTexture::~GLTexture()
|
||||||
{
|
{
|
||||||
delete m_vbo;
|
|
||||||
discard();
|
|
||||||
assert(mUnnormalizeActive == 0);
|
|
||||||
assert(mNormalizeActive == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::init()
|
GLTexture& GLTexture::operator = (const GLTexture& tex)
|
||||||
{
|
{
|
||||||
mTexture = None;
|
d_ptr = tex.d_ptr;
|
||||||
mTarget = 0;
|
return *this;
|
||||||
mFilter = 0;
|
}
|
||||||
y_inverted = false;
|
|
||||||
can_use_mipmaps = false;
|
GLTexturePrivate::GLTexturePrivate()
|
||||||
has_valid_mipmaps = false;
|
{
|
||||||
mUnnormalizeActive = 0;
|
m_texture = 0;
|
||||||
mNormalizeActive = 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;
|
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
|
#ifdef KWIN_HAVE_OPENGLES
|
||||||
sNPOTTextureSupported = true;
|
sNPOTTextureSupported = true;
|
||||||
|
@ -129,22 +153,28 @@ void GLTexture::initStatic()
|
||||||
|
|
||||||
bool GLTexture::isNull() const
|
bool GLTexture::isNull() const
|
||||||
{
|
{
|
||||||
return mTexture == None;
|
Q_D(const GLTexture);
|
||||||
|
return None == d->m_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize GLTexture::size() const
|
QSize GLTexture::size() const
|
||||||
{
|
{
|
||||||
return mSize;
|
Q_D(const GLTexture);
|
||||||
|
return d->m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLTexture::load(const QImage& image, GLenum target)
|
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())
|
if (image.isNull())
|
||||||
return false;
|
return false;
|
||||||
QImage img = image;
|
QImage img = image;
|
||||||
mTarget = target;
|
d->m_target = target;
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
#ifndef KWIN_HAVE_OPENGLES
|
||||||
if (mTarget != GL_TEXTURE_RECTANGLE_ARB) {
|
if (d->m_target != GL_TEXTURE_RECTANGLE_ARB) {
|
||||||
#endif
|
#endif
|
||||||
if (!NPOTTextureSupported()
|
if (!NPOTTextureSupported()
|
||||||
&& (!isPowerOfTwo(image.width()) || !isPowerOfTwo(image.height()))) {
|
&& (!isPowerOfTwo(image.width()) || !isPowerOfTwo(image.height()))) {
|
||||||
|
@ -152,33 +182,33 @@ bool GLTexture::load(const QImage& image, GLenum target)
|
||||||
img = img.scaled(nearestPowerOfTwo(image.width()),
|
img = img.scaled(nearestPowerOfTwo(image.width()),
|
||||||
nearestPowerOfTwo(image.height()));
|
nearestPowerOfTwo(image.height()));
|
||||||
}
|
}
|
||||||
mScale.setWidth(1.0 / img.width());
|
d->m_scale.setWidth(1.0 / img.width());
|
||||||
mScale.setHeight(1.0 / img.height());
|
d->m_scale.setHeight(1.0 / img.height());
|
||||||
can_use_mipmaps = true;
|
d->m_canUseMipmaps = true;
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
#ifndef KWIN_HAVE_OPENGLES
|
||||||
} else {
|
} else {
|
||||||
mScale.setWidth(1.0);
|
d->m_scale.setWidth(1.0);
|
||||||
mScale.setHeight(1.0);
|
d->m_scale.setHeight(1.0);
|
||||||
can_use_mipmaps = false;
|
d->m_canUseMipmaps = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
setFilter(GL_LINEAR);
|
setFilter(GL_LINEAR);
|
||||||
mSize = img.size();
|
d->m_size = img.size();
|
||||||
y_inverted = true;
|
d->m_yInverted = true;
|
||||||
|
|
||||||
img = convertToGLFormat(img);
|
img = d->convertToGLFormat(img);
|
||||||
|
|
||||||
setDirty();
|
setDirty();
|
||||||
if (isNull()) {
|
if (isNull()) {
|
||||||
glGenTextures(1, &mTexture);
|
glGenTextures(1, &d->m_texture);
|
||||||
}
|
}
|
||||||
bind();
|
bind();
|
||||||
#ifdef KWIN_HAVE_OPENGLES
|
#ifdef KWIN_HAVE_OPENGLES
|
||||||
// format and internal format have to match in ES, GL_RGBA8 and GL_BGRA are not available
|
// 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
|
// 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
|
#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());
|
GL_BGRA, GL_UNSIGNED_BYTE, img.bits());
|
||||||
#endif
|
#endif
|
||||||
unbind();
|
unbind();
|
||||||
|
@ -201,37 +231,47 @@ bool GLTexture::load(const QString& fileName)
|
||||||
|
|
||||||
void GLTexture::discard()
|
void GLTexture::discard()
|
||||||
{
|
{
|
||||||
setDirty();
|
d_ptr = new GLTexturePrivate();
|
||||||
if (mTexture != None)
|
}
|
||||||
glDeleteTextures(1, &mTexture);
|
|
||||||
mTexture = None;
|
void GLTexturePrivate::bind()
|
||||||
|
{
|
||||||
|
#ifndef KWIN_HAVE_OPENGLES
|
||||||
|
glEnable(m_target);
|
||||||
|
#endif
|
||||||
|
glBindTexture(m_target, m_texture);
|
||||||
|
enableFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::bind()
|
void GLTexture::bind()
|
||||||
{
|
{
|
||||||
|
Q_D(GLTexture);
|
||||||
|
d->bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLTexturePrivate::unbind()
|
||||||
|
{
|
||||||
|
glBindTexture(m_target, 0);
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
#ifndef KWIN_HAVE_OPENGLES
|
||||||
glEnable(mTarget);
|
glDisable(m_target);
|
||||||
#endif
|
#endif
|
||||||
glBindTexture(mTarget, mTexture);
|
|
||||||
enableFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::unbind()
|
void GLTexture::unbind()
|
||||||
{
|
{
|
||||||
glBindTexture(mTarget, 0);
|
Q_D(GLTexture);
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
d->unbind();
|
||||||
glDisable(mTarget);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::render(QRegion region, const QRect& rect)
|
void GLTexture::render(QRegion region, const QRect& rect)
|
||||||
{
|
{
|
||||||
if (rect.size() != m_cachedSize) {
|
Q_D(GLTexture);
|
||||||
m_cachedSize = rect.size();
|
if (rect.size() != d->m_cachedSize) {
|
||||||
|
d->m_cachedSize = rect.size();
|
||||||
QRect r(rect);
|
QRect r(rect);
|
||||||
r.moveTo(0, 0);
|
r.moveTo(0, 0);
|
||||||
if (!m_vbo) {
|
if (!d->m_vbo) {
|
||||||
m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static);
|
d->m_vbo = new GLVertexBuffer(KWin::GLVertexBuffer::Static);
|
||||||
}
|
}
|
||||||
const float verts[ 4 * 2 ] = {
|
const float verts[ 4 * 2 ] = {
|
||||||
// NOTICE: r.x/y could be replaced by "0", but that would make it unreadable...
|
// 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()
|
r.x() + rect.width(), r.y() + rect.height()
|
||||||
};
|
};
|
||||||
const float texcoords[ 4 * 2 ] = {
|
const float texcoords[ 4 * 2 ] = {
|
||||||
0.0f, y_inverted ? 0.0f : 1.0f, // y needs to be swapped (normalized coords)
|
0.0f, d->m_yInverted ? 0.0f : 1.0f, // y needs to be swapped (normalized coords)
|
||||||
0.0f, y_inverted ? 1.0f : 0.0f,
|
0.0f, d->m_yInverted ? 1.0f : 0.0f,
|
||||||
1.0f, y_inverted ? 0.0f : 1.0f,
|
1.0f, d->m_yInverted ? 0.0f : 1.0f,
|
||||||
1.0f, y_inverted ? 1.0f : 0.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;
|
QMatrix4x4 translation;
|
||||||
translation.translate(rect.x(), rect.y());
|
translation.translate(rect.x(), rect.y());
|
||||||
|
@ -257,7 +297,7 @@ void GLTexture::render(QRegion region, const QRect& rect)
|
||||||
} else {
|
} else {
|
||||||
pushMatrix(translation);
|
pushMatrix(translation);
|
||||||
}
|
}
|
||||||
m_vbo->render(region, GL_TRIANGLE_STRIP);
|
d->m_vbo->render(region, GL_TRIANGLE_STRIP);
|
||||||
if (ShaderManager::instance()->isShaderBound()) {
|
if (ShaderManager::instance()->isShaderBound()) {
|
||||||
GLShader *shader = ShaderManager::instance()->getBoundShader();
|
GLShader *shader = ShaderManager::instance()->getBoundShader();
|
||||||
shader->setUniform(GLShader::WindowTransformation, QMatrix4x4());
|
shader->setUniform(GLShader::WindowTransformation, QMatrix4x4());
|
||||||
|
@ -268,85 +308,94 @@ void GLTexture::render(QRegion region, const QRect& rect)
|
||||||
|
|
||||||
GLuint GLTexture::texture() const
|
GLuint GLTexture::texture() const
|
||||||
{
|
{
|
||||||
return mTexture;
|
Q_D(const GLTexture);
|
||||||
|
return d->m_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum GLTexture::target() const
|
GLenum GLTexture::target() const
|
||||||
{
|
{
|
||||||
return mTarget;
|
Q_D(const GLTexture);
|
||||||
|
return d->m_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum GLTexture::filter() const
|
GLenum GLTexture::filter() const
|
||||||
{
|
{
|
||||||
return mFilter;
|
Q_D(const GLTexture);
|
||||||
|
return d->m_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLTexture::isDirty() const
|
bool GLTexture::isDirty() const
|
||||||
{
|
{
|
||||||
return has_valid_mipmaps;
|
Q_D(const GLTexture);
|
||||||
|
return d->m_hasValidMipmaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::setTexture(GLuint texture)
|
void GLTexture::setTexture(GLuint texture)
|
||||||
{
|
{
|
||||||
|
Q_D(GLTexture);
|
||||||
discard();
|
discard();
|
||||||
mTexture = texture;
|
d->m_texture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::setTarget(GLenum target)
|
void GLTexture::setTarget(GLenum target)
|
||||||
{
|
{
|
||||||
mTarget = target;
|
Q_D(GLTexture);
|
||||||
|
d->m_target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::setFilter(GLenum filter)
|
void GLTexture::setFilter(GLenum filter)
|
||||||
{
|
{
|
||||||
mFilter = filter;
|
Q_D(GLTexture);
|
||||||
|
d->m_filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::setWrapMode(GLenum mode)
|
void GLTexture::setWrapMode(GLenum mode)
|
||||||
{
|
{
|
||||||
|
Q_D(GLTexture);
|
||||||
bind();
|
bind();
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, mode);
|
glTexParameteri(d->m_target, GL_TEXTURE_WRAP_S, mode);
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, mode);
|
glTexParameteri(d->m_target, GL_TEXTURE_WRAP_T, mode);
|
||||||
unbind();
|
unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::setDirty()
|
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?
|
// trilinear filtering requested, but is it possible?
|
||||||
if (NPOTTextureSupported()
|
if (sNPOTTextureSupported
|
||||||
&& framebufferObjectSupported()
|
&& sFramebufferObjectSupported
|
||||||
&& can_use_mipmaps) {
|
&& m_canUseMipmaps) {
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
if (!has_valid_mipmaps) {
|
if (!m_hasValidMipmaps) {
|
||||||
glGenerateMipmap(mTarget);
|
glGenerateMipmap(m_target);
|
||||||
has_valid_mipmaps = true;
|
m_hasValidMipmaps = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can't use trilinear, so use bilinear
|
// can't use trilinear, so use bilinear
|
||||||
setFilter(GL_LINEAR);
|
m_filter = GL_LINEAR;
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
}
|
}
|
||||||
} else if (mFilter == GL_LINEAR) {
|
} else if (m_filter == GL_LINEAR) {
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
} else {
|
} else {
|
||||||
// if neither trilinear nor bilinear, default to fast filtering
|
// if neither trilinear nor bilinear, default to fast filtering
|
||||||
setFilter(GL_NEAREST);
|
m_filter = GL_NEAREST;
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(mTarget, GL_TEXTURE_MAG_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()
|
// Copied from Qt's QGLWidget::convertToGLFormat()
|
||||||
QImage res;
|
QImage res;
|
||||||
|
@ -410,7 +459,52 @@ QImage GLTexture::convertToGLFormat(const QImage& img) const
|
||||||
|
|
||||||
bool GLTexture::isYInverted() 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
|
} // namespace KWin
|
||||||
|
|
|
@ -25,7 +25,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "kwinglobals.h"
|
#include "kwinglobals.h"
|
||||||
|
|
||||||
#include <QtCore/QSize>
|
#include <QtCore/QSize>
|
||||||
#include <QtCore/QSharedData>
|
#include <QSharedPointer>
|
||||||
|
#include <QExplicitlySharedDataPointer>
|
||||||
|
|
||||||
class QImage;
|
class QImage;
|
||||||
class QPixmap;
|
class QPixmap;
|
||||||
|
@ -37,37 +38,45 @@ namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
class GLVertexBuffer;
|
class GLVertexBuffer;
|
||||||
|
class GLTexturePrivate;
|
||||||
|
|
||||||
class KWIN_EXPORT GLTexture
|
class KWIN_EXPORT GLTexture
|
||||||
: public QSharedData
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GLTexture();
|
GLTexture();
|
||||||
|
GLTexture(const GLTexture& tex);
|
||||||
explicit GLTexture(const QImage& image, GLenum target = GL_TEXTURE_2D);
|
explicit GLTexture(const QImage& image, GLenum target = GL_TEXTURE_2D);
|
||||||
explicit GLTexture(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
|
explicit GLTexture(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
|
||||||
GLTexture(const QString& fileName);
|
GLTexture(const QString& fileName);
|
||||||
GLTexture(int width, int height);
|
GLTexture(int width, int height);
|
||||||
virtual ~GLTexture();
|
virtual ~GLTexture();
|
||||||
|
|
||||||
|
GLTexture & operator = (const GLTexture& tex);
|
||||||
|
|
||||||
bool isNull() const;
|
bool isNull() const;
|
||||||
QSize size() const;
|
QSize size() const;
|
||||||
int width() const {
|
int width() const;
|
||||||
return mSize.width(); /// @since 4.5
|
int height() const;
|
||||||
}
|
|
||||||
int height() const {
|
|
||||||
return mSize.height(); /// @since 4.5
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @since 4.7
|
* @since 4.7
|
||||||
**/
|
**/
|
||||||
bool isYInverted() const;
|
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 QImage& image, GLenum target = GL_TEXTURE_2D);
|
||||||
virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
|
virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
|
||||||
virtual bool load(const QString& fileName);
|
virtual bool load(const QString& fileName);
|
||||||
virtual void discard();
|
virtual void discard();
|
||||||
virtual void bind();
|
void bind();
|
||||||
virtual void unbind();
|
void unbind();
|
||||||
|
/**
|
||||||
|
* Release the data which is bound to the texture.
|
||||||
|
* @since 4.8
|
||||||
|
**/
|
||||||
|
void release();
|
||||||
void render(QRegion region, const QRect& rect);
|
void render(QRegion region, const QRect& rect);
|
||||||
|
|
||||||
GLuint texture() const;
|
GLuint texture() const;
|
||||||
|
@ -80,41 +89,19 @@ public:
|
||||||
void setWrapMode(GLenum mode);
|
void setWrapMode(GLenum mode);
|
||||||
virtual void setDirty();
|
virtual void setDirty();
|
||||||
|
|
||||||
static void initStatic();
|
static bool NPOTTextureSupported();
|
||||||
static bool NPOTTextureSupported() {
|
static bool framebufferObjectSupported();
|
||||||
return sNPOTTextureSupported;
|
static bool saturationSupported();
|
||||||
}
|
|
||||||
static bool framebufferObjectSupported() {
|
|
||||||
return sFramebufferObjectSupported;
|
|
||||||
}
|
|
||||||
static bool saturationSupported() {
|
|
||||||
return sSaturationSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void enableFilter();
|
void enableFilter();
|
||||||
QImage convertToGLFormat(const QImage& img) const;
|
QImage convertToGLFormat(const QImage& img) const;
|
||||||
|
|
||||||
GLuint mTexture;
|
QExplicitlySharedDataPointer<GLTexturePrivate> d_ptr;
|
||||||
GLenum mTarget;
|
GLTexture(GLTexturePrivate& dd);
|
||||||
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;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
Q_DECLARE_PRIVATE(GLTexture);
|
||||||
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)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
74
libkwineffects/kwingltexture_p.h
Normal file
74
libkwineffects/kwingltexture_p.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>
|
||||||
|
Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
Copyright (C) 2011 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
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef KWIN_GLTEXTURE_P_H
|
||||||
|
#define KWIN_GLTEXTURE_P_H
|
||||||
|
|
||||||
|
#include "kwinconfig.h" // KWIN_HAVE_OPENGL
|
||||||
|
#include "kwinglobals.h"
|
||||||
|
|
||||||
|
#include <QtCore/QSize>
|
||||||
|
#include <QtCore/QSharedData>
|
||||||
|
|
||||||
|
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
|
|
@ -21,6 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "kwinglutils.h"
|
#include "kwinglutils.h"
|
||||||
|
|
||||||
|
// need to call GLTexturePrivate::initStatic()
|
||||||
|
#include "kwingltexture_p.h"
|
||||||
|
|
||||||
#include "kwinglobals.h"
|
#include "kwinglobals.h"
|
||||||
#include "kwineffects.h"
|
#include "kwineffects.h"
|
||||||
#include "kwinglplatform.h"
|
#include "kwinglplatform.h"
|
||||||
|
@ -113,7 +116,7 @@ void initGL()
|
||||||
// handle OpenGL extensions functions
|
// handle OpenGL extensions functions
|
||||||
glResolveFunctions();
|
glResolveFunctions();
|
||||||
|
|
||||||
GLTexture::initStatic();
|
GLTexturePrivate::initStatic();
|
||||||
GLRenderTarget::initStatic();
|
GLRenderTarget::initStatic();
|
||||||
GLVertexBuffer::initStatic();
|
GLVertexBuffer::initStatic();
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,55 +268,43 @@ void SceneOpenGL::windowOpacityChanged(KWin::Toplevel* t)
|
||||||
// SceneOpenGL::Texture
|
// 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);
|
load(pix, size, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target)
|
SceneOpenGL::Texture::Texture(const QPixmap& pix, GLenum target)
|
||||||
: GLTexture()
|
: GLTexture(*new TexturePrivate())
|
||||||
{
|
{
|
||||||
init();
|
|
||||||
load(pix, target);
|
load(pix, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneOpenGL::Texture::~Texture()
|
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()
|
void SceneOpenGL::Texture::discard()
|
||||||
{
|
{
|
||||||
if (mTexture != None)
|
d_ptr = new TexturePrivate();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
|
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)
|
bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target)
|
||||||
{
|
{
|
||||||
|
Q_D(Texture);
|
||||||
if (pixmap.isNull())
|
if (pixmap.isNull())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -345,8 +334,6 @@ bool SceneOpenGL::Texture::load(const QPixmap& pixmap, GLenum target)
|
||||||
return GLTexture::load(pixmap.toImage(), target);
|
return GLTexture::load(pixmap.toImage(), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
y_inverted = true;
|
|
||||||
|
|
||||||
// use the X11 pixmap provided by Qt
|
// use the X11 pixmap provided by Qt
|
||||||
return load(pixmap.handle(), pixmap.size(), pixmap.depth());
|
return load(pixmap.handle(), pixmap.size(), pixmap.depth());
|
||||||
}
|
}
|
||||||
|
@ -374,9 +361,16 @@ SceneOpenGL::Window::~Window()
|
||||||
bool SceneOpenGL::Window::bindTexture()
|
bool SceneOpenGL::Window::bindTexture()
|
||||||
{
|
{
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
#ifndef KWIN_HAVE_OPENGLES
|
||||||
if (texture.texture() != None && toplevel->damage().isEmpty()) {
|
if (!texture.isNull()) {
|
||||||
// texture doesn't need updating, just bind it
|
if (toplevel->damage().isEmpty()) {
|
||||||
glBindTexture(texture.target(), texture.texture());
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -384,8 +378,10 @@ bool SceneOpenGL::Window::bindTexture()
|
||||||
Pixmap pix = toplevel->windowPixmap();
|
Pixmap pix = toplevel->windowPixmap();
|
||||||
if (pix == None)
|
if (pix == None)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool success = texture.load(pix, toplevel->size(), toplevel->depth(),
|
bool success = texture.load(pix, toplevel->size(), toplevel->depth(),
|
||||||
toplevel->damage());
|
toplevel->damage());
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize()));
|
toplevel->resetDamage(QRect(toplevel->clientPos(), toplevel->clientSize()));
|
||||||
else
|
else
|
||||||
|
@ -417,7 +413,7 @@ void SceneOpenGL::Window::checkTextureSize()
|
||||||
// when the window's composite pixmap is discarded, undo binding it to the texture
|
// when the window's composite pixmap is discarded, undo binding it to the texture
|
||||||
void SceneOpenGL::Window::pixmapDiscarded()
|
void SceneOpenGL::Window::pixmapDiscarded()
|
||||||
{
|
{
|
||||||
texture.release();
|
texture.discard();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData &data) const
|
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) {
|
if (decorationTexture->texture() != None && !updateDeco) {
|
||||||
// texture doesn't need updating, just bind it
|
// texture doesn't need updating, just bind it
|
||||||
glBindTexture(decorationTexture->target(), decorationTexture->texture());
|
decorationTexture->bind();
|
||||||
} else if (!decoration->isNull()) {
|
} else if (!decoration->isNull()) {
|
||||||
bool success = decorationTexture->load(*decoration);
|
bool success = decorationTexture->load(*decoration);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "shadow.h"
|
#include "shadow.h"
|
||||||
|
|
||||||
#include "kwinglutils.h"
|
#include "kwinglutils.h"
|
||||||
|
#include "kwingltexture_p.h"
|
||||||
|
|
||||||
#ifdef HAVE_XSHM
|
#ifdef HAVE_XSHM
|
||||||
#include <X11/extensions/XShm.h>
|
#include <X11/extensions/XShm.h>
|
||||||
|
@ -41,6 +42,7 @@ class SceneOpenGL
|
||||||
public:
|
public:
|
||||||
class EffectFrame;
|
class EffectFrame;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
class TexturePrivate;
|
||||||
class Window;
|
class Window;
|
||||||
SceneOpenGL(Workspace* ws);
|
SceneOpenGL(Workspace* ws);
|
||||||
virtual ~SceneOpenGL();
|
virtual ~SceneOpenGL();
|
||||||
|
@ -100,40 +102,51 @@ private:
|
||||||
bool debug;
|
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
|
class SceneOpenGL::Texture
|
||||||
: public GLTexture
|
: public GLTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Texture();
|
Texture();
|
||||||
|
Texture(const Texture& tex);
|
||||||
Texture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
|
Texture(const QPixmap& pix, GLenum target = GL_TEXTURE_2D);
|
||||||
virtual ~Texture();
|
virtual ~Texture();
|
||||||
|
|
||||||
|
Texture & operator = (const Texture& tex);
|
||||||
|
|
||||||
using GLTexture::load;
|
using GLTexture::load;
|
||||||
virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D);
|
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 QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
|
||||||
virtual void discard();
|
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:
|
protected:
|
||||||
Texture(const Pixmap& pix, const QSize& size, int depth);
|
Texture(const Pixmap& pix, const QSize& size, int depth);
|
||||||
void findTarget();
|
void findTarget();
|
||||||
QRegion optimizeBindDamage(const QRegion& reg, int limit);
|
|
||||||
void createTexture();
|
|
||||||
virtual bool load(const Pixmap& pix, const QSize& size, int depth,
|
virtual bool load(const Pixmap& pix, const QSize& size, int depth,
|
||||||
QRegion region);
|
QRegion region);
|
||||||
virtual bool load(const Pixmap& pix, const QSize& size, int depth);
|
virtual bool load(const Pixmap& pix, const QSize& size, int depth);
|
||||||
|
|
||||||
private:
|
Texture(TexturePrivate& dd);
|
||||||
void init();
|
|
||||||
|
|
||||||
#ifndef KWIN_HAVE_OPENGLES
|
private:
|
||||||
GLXPixmap glxpixmap; // the glx pixmap the texture is bound to, only for tfp_mode
|
Q_DECLARE_PRIVATE(Texture);
|
||||||
#endif
|
|
||||||
|
|
||||||
friend class SceneOpenGL::Window;
|
friend class SceneOpenGL::Window;
|
||||||
};
|
};
|
||||||
|
|
|
@ -210,65 +210,71 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
|
||||||
// SceneOpenGL::Texture
|
// 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()
|
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,
|
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
|
||||||
int depth, QRegion region)
|
int depth, QRegion region)
|
||||||
{
|
{
|
||||||
|
// decrease the reference counter for the old texture
|
||||||
|
d_ptr = new TexturePrivate();
|
||||||
|
|
||||||
|
Q_D(Texture);
|
||||||
Q_UNUSED(depth)
|
Q_UNUSED(depth)
|
||||||
Q_UNUSED(region)
|
Q_UNUSED(region)
|
||||||
|
|
||||||
if (pix == None)
|
if (pix == None)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mTexture == None) {
|
glGenTextures(1, &d->m_texture);
|
||||||
createTexture();
|
bind();
|
||||||
bind();
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
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_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
const EGLint attribs[] = {
|
||||||
const EGLint attribs[] = {
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
EGL_NONE
|
||||||
EGL_NONE
|
};
|
||||||
};
|
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
||||||
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
(EGLClientBuffer)pix, attribs);
|
||||||
(EGLClientBuffer)pix, attribs);
|
|
||||||
|
|
||||||
if (EGL_NO_IMAGE_KHR == image) {
|
if (EGL_NO_IMAGE_KHR == image) {
|
||||||
kDebug(1212) << "failed to create egl image";
|
kDebug(1212) << "failed to create egl image";
|
||||||
unbind();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
|
|
||||||
eglDestroyImageKHR(dpy, image);
|
|
||||||
unbind();
|
unbind();
|
||||||
checkGLError("load texture");
|
return false;
|
||||||
setYInverted(true);
|
|
||||||
mSize = size;
|
|
||||||
}
|
}
|
||||||
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
|
||||||
|
eglDestroyImageKHR(dpy, image);
|
||||||
|
unbind();
|
||||||
|
checkGLError("load texture");
|
||||||
|
setYInverted(true);
|
||||||
|
d->m_size = size;
|
||||||
return true;
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ SceneOpenGL::SceneOpenGL(Workspace* ws)
|
||||||
return; // error
|
return; // error
|
||||||
// Initialize OpenGL
|
// Initialize OpenGL
|
||||||
initGL();
|
initGL();
|
||||||
|
|
||||||
GLPlatform *glPlatform = GLPlatform::instance();
|
GLPlatform *glPlatform = GLPlatform::instance();
|
||||||
if (glPlatform->isSoftwareEmulation()) {
|
if (glPlatform->isSoftwareEmulation()) {
|
||||||
kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender.";
|
kError(1212) << "OpenGL Software Rasterizer detected. Falling back to XRender.";
|
||||||
|
@ -544,33 +545,38 @@ void SceneOpenGL::flushBuffer(int mask, QRegion damage)
|
||||||
// SceneOpenGL::Texture
|
// 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) {
|
if (!options->glStrictBinding) {
|
||||||
glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT);
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
||||||
}
|
}
|
||||||
glXDestroyPixmap(display(), glxpixmap);
|
glXDestroyPixmap(display(), m_glxpixmap);
|
||||||
glxpixmap = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneOpenGL::Texture::findTarget()
|
void SceneOpenGL::Texture::findTarget()
|
||||||
{
|
{
|
||||||
|
Q_D(Texture);
|
||||||
unsigned int new_target = 0;
|
unsigned int new_target = 0;
|
||||||
if (glXQueryDrawable && glxpixmap != None)
|
if (glXQueryDrawable && d->m_glxpixmap != None)
|
||||||
glXQueryDrawable(display(), glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
|
glXQueryDrawable(display(), d->m_glxpixmap, GLX_TEXTURE_TARGET_EXT, &new_target);
|
||||||
// HACK: this used to be a hack for Xgl.
|
// HACK: this used to be a hack for Xgl.
|
||||||
// without this hack the NVIDIA blob aborts when trying to bind a texture from
|
// without this hack the NVIDIA blob aborts when trying to bind a texture from
|
||||||
// a pixmap icon
|
// a pixmap icon
|
||||||
if (new_target == 0) {
|
if (new_target == 0) {
|
||||||
if (NPOTTextureSupported() ||
|
if (NPOTTextureSupported() ||
|
||||||
(isPowerOfTwo(mSize.width()) && isPowerOfTwo(mSize.height()))) {
|
(isPowerOfTwo(d->m_size.width()) && isPowerOfTwo(d->m_size.height()))) {
|
||||||
new_target = GLX_TEXTURE_2D_EXT;
|
new_target = GLX_TEXTURE_2D_EXT;
|
||||||
} else {
|
} else {
|
||||||
new_target = GLX_TEXTURE_RECTANGLE_EXT;
|
new_target = GLX_TEXTURE_RECTANGLE_EXT;
|
||||||
|
@ -578,14 +584,14 @@ void SceneOpenGL::Texture::findTarget()
|
||||||
}
|
}
|
||||||
switch(new_target) {
|
switch(new_target) {
|
||||||
case GLX_TEXTURE_2D_EXT:
|
case GLX_TEXTURE_2D_EXT:
|
||||||
mTarget = GL_TEXTURE_2D;
|
d->m_target = GL_TEXTURE_2D;
|
||||||
mScale.setWidth(1.0f / mSize.width());
|
d->m_scale.setWidth(1.0f / d->m_size.width());
|
||||||
mScale.setHeight(1.0f / mSize.height());
|
d->m_scale.setHeight(1.0f / d->m_size.height());
|
||||||
break;
|
break;
|
||||||
case GLX_TEXTURE_RECTANGLE_EXT:
|
case GLX_TEXTURE_RECTANGLE_EXT:
|
||||||
mTarget = GL_TEXTURE_RECTANGLE_ARB;
|
d->m_target = GL_TEXTURE_RECTANGLE_ARB;
|
||||||
mScale.setWidth(1.0f);
|
d->m_scale.setWidth(1.0f);
|
||||||
mScale.setHeight(1.0f);
|
d->m_scale.setHeight(1.0f);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
|
@ -595,6 +601,10 @@ void SceneOpenGL::Texture::findTarget()
|
||||||
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
|
bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
|
||||||
int depth, QRegion region)
|
int depth, QRegion region)
|
||||||
{
|
{
|
||||||
|
// decrease the reference counter for the old texture
|
||||||
|
d_ptr = new TexturePrivate();
|
||||||
|
|
||||||
|
Q_D(Texture);
|
||||||
#ifdef CHECK_GL_ERROR
|
#ifdef CHECK_GL_ERROR
|
||||||
checkGLError("TextureLoad1");
|
checkGLError("TextureLoad1");
|
||||||
#endif
|
#endif
|
||||||
|
@ -606,78 +616,65 @@ bool SceneOpenGL::Texture::load(const Pixmap& pix, const QSize& size,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSize = size;
|
d->m_size = size;
|
||||||
if (mTexture == None || !region.isEmpty()) {
|
d->m_yInverted = true;
|
||||||
// new texture, or texture contents changed; mipmaps now invalid
|
// new texture, or texture contents changed; mipmaps now invalid
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CHECK_GL_ERROR
|
#ifdef CHECK_GL_ERROR
|
||||||
checkGLError("TextureLoad2");
|
checkGLError("TextureLoad2");
|
||||||
#endif
|
#endif
|
||||||
// tfp mode, simply bind the pixmap to texture
|
// tfp mode, simply bind the pixmap to texture
|
||||||
if (mTexture == None)
|
glGenTextures(1, &d->m_texture);
|
||||||
createTexture();
|
|
||||||
// The GLX pixmap references the contents of the original pixmap, so it doesn't
|
// The GLX pixmap references the contents of the original pixmap, so it doesn't
|
||||||
// need to be recreated when the contents change.
|
// need to be recreated when the contents change.
|
||||||
// The texture may or may not use the same storage depending on the EXT_tfp
|
// The texture may or may not use the same storage depending on the EXT_tfp
|
||||||
// implementation. When options->glStrictBinding is true, the texture uses
|
// implementation. When options->glStrictBinding is true, the texture uses
|
||||||
// a different storage and needs to be updated with a call to
|
// a different storage and needs to be updated with a call to
|
||||||
// glXBindTexImageEXT() when the contents of the pixmap has changed.
|
// glXBindTexImageEXT() when the contents of the pixmap has changed.
|
||||||
if (glxpixmap != None)
|
int attrs[] = {
|
||||||
glBindTexture(mTarget, mTexture);
|
GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
|
||||||
else {
|
GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap,
|
||||||
int attrs[] = {
|
None, None, None
|
||||||
GLX_TEXTURE_FORMAT_EXT, fbcdrawableinfo[ depth ].bind_texture_format,
|
};
|
||||||
GLX_MIPMAP_TEXTURE_EXT, fbcdrawableinfo[ depth ].mipmap,
|
// Specifying the texture target explicitly is reported to cause a performance
|
||||||
None, None, None
|
// regression with R300G (see bug #256654).
|
||||||
};
|
if (GLPlatform::instance()->driver() != Driver_R300G) {
|
||||||
// Specifying the texture target explicitly is reported to cause a performance
|
if ((fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
|
||||||
// regression with R300G (see bug #256654).
|
(GLTexture::NPOTTextureSupported() ||
|
||||||
if (GLPlatform::instance()->driver() != Driver_R300G) {
|
(isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
|
||||||
if ((fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_2D_BIT_EXT) &&
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
||||||
(GLTexture::NPOTTextureSupported() ||
|
attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
|
||||||
(isPowerOfTwo(size.width()) && isPowerOfTwo(size.height())))) {
|
} else if (fbcdrawableinfo[ depth ].texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT) {
|
||||||
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
attrs[ 4 ] = GLX_TEXTURE_TARGET_EXT;
|
||||||
attrs[ 5 ] = GLX_TEXTURE_2D_EXT;
|
attrs[ 5 ] = GLX_TEXTURE_RECTANGLE_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)
|
d->m_glxpixmap = glXCreatePixmap(display(), fbcdrawableinfo[ depth ].fbconfig, pix, attrs);
|
||||||
// Mark the texture as damaged so it will be updated on the next call to bind()
|
#ifdef CHECK_GL_ERROR
|
||||||
glXBindTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
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
|
#ifdef CHECK_GL_ERROR
|
||||||
checkGLError("TextureLoad0");
|
checkGLError("TextureLoad0");
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneOpenGL::Texture::bind()
|
void SceneOpenGL::TexturePrivate::bind()
|
||||||
{
|
{
|
||||||
glEnable(mTarget);
|
GLTexturePrivate::bind();
|
||||||
glBindTexture(mTarget, mTexture);
|
if (options->glStrictBinding && m_glxpixmap) {
|
||||||
// if one of the GLTexture::load functions is called, the glxpixmap doesn't
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
||||||
// have to exist
|
glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||||
if (options->glStrictBinding && glxpixmap) {
|
m_hasValidMipmaps = false; // Mipmaps have to be regenerated after updating the texture
|
||||||
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
|
|
||||||
}
|
}
|
||||||
enableFilter();
|
enableFilter();
|
||||||
if (hasGLVersion(1, 4, 0)) {
|
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)) {
|
if (hasGLVersion(1, 4, 0)) {
|
||||||
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f);
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||||
}
|
}
|
||||||
// if one of the GLTexture::load functions is called, the glxpixmap doesn't
|
if (options->glStrictBinding && m_glxpixmap) {
|
||||||
// have to exist
|
glBindTexture(m_target, m_texture);
|
||||||
if (options->glStrictBinding && glxpixmap) {
|
glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
|
||||||
glBindTexture(mTarget, mTexture);
|
|
||||||
glXReleaseTexImageEXT(display(), glxpixmap, GLX_FRONT_LEFT_EXT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture::unbind();
|
GLTexturePrivate::unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue