OpenGLPaintRedirector updates textures directly

Ownership of decoration textures is moved from SceneOpenGL::Window to
OpenGLPaintRedirector. The PaintRedirector is responsible for updating
the textures whenever they change. For this GLTexture is extended by an
update(QImage, QPoint) method which uses glTexSubImage2D to update only
the changed parts.

The big advantage compared to before is that if e.g. only a button is
animated only the button part is updated instead of the complete deco
part.
This commit is contained in:
Martin Gräßlin 2013-02-22 10:13:46 +01:00
parent fba7504063
commit aa549f45d5
6 changed files with 122 additions and 107 deletions

View file

@ -218,6 +218,52 @@ bool GLTexture::load(const QImage& image, GLenum target)
return true;
}
void GLTexture::update(const QImage &image, const QPoint &offset, const QRect &src)
{
if (image.isNull() || isNull())
return;
Q_D(GLTexture);
#ifdef KWIN_HAVE_OPENGLES
static bool s_supportsUnpack = hasGLExtension("GL_EXT_unpack_subimage");
#else
static bool s_supportsUnpack = true;
#endif
int width = image.width();
int height = image.height();
QImage tmpImage;
if (!src.isNull()) {
if (s_supportsUnpack) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.width());
glPixelStorei(GL_UNPACK_SKIP_PIXELS, src.x());
glPixelStorei(GL_UNPACK_SKIP_ROWS, src.y());
} else {
tmpImage = image.copy(src);
}
width = src.width();
height = src.height();
}
const QImage &img = d->convertToGLFormat(tmpImage.isNull() ? image : tmpImage);
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
glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
#else
glTexSubImage2D(d->m_target, 0, offset.x(), offset.y(), width, height, GL_BGRA, GL_UNSIGNED_BYTE, img.bits());
#endif
checkGLError("update texture");
unbind();
setDirty();
if (!src.isNull() && s_supportsUnpack) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
}
}
bool GLTexture::load(const QPixmap& pixmap, GLenum target)
{
if (pixmap.isNull())
@ -459,8 +505,10 @@ QImage GLTexturePrivate::convertToGLFormat(const QImage& img) const
q++;
}
}
} else {
} else if (img.format() != QImage::Format_ARGB32_Premultiplied) {
res = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
} else {
return img;
}
#endif
return res;

View file

@ -70,6 +70,7 @@ public:
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);
void update(const QImage& image, const QPoint &offset = QPoint(0, 0), const QRect &src = QRect());
virtual void discard();
void bind();
void unbind();

View file

@ -28,6 +28,7 @@ DEALINGS IN THE SOFTWARE.
#include "client.h"
#include "deleted.h"
#include "effects.h"
#include <kwinglutils.h>
#include <kwinxrenderutils.h>
#include <kdebug.h>
#include <QPaintEngine>
@ -228,7 +229,7 @@ void PaintRedirector::resizePixmaps()
}
}
const QPixmap *PaintRedirector::pixmap(PaintRedirector::DecorationPixmap border) const
GLTexture *PaintRedirector::texture(PaintRedirector::DecorationPixmap border) const
{
Q_UNUSED(border)
return NULL;
@ -243,34 +244,45 @@ xcb_render_picture_t PaintRedirector::picture(PaintRedirector::DecorationPixmap
OpenGLPaintRedirector::OpenGLPaintRedirector(Client *c, QWidget *widget)
: PaintRedirector(c, widget)
{
for (int i=0; i<PixmapCount; ++i) {
m_textures[i] = NULL;
}
resizePixmaps();
}
OpenGLPaintRedirector::~OpenGLPaintRedirector()
{
for (int i=0; i<PixmapCount; ++i) {
delete m_textures[i];
}
}
const QPixmap *OpenGLPaintRedirector::pixmap(PaintRedirector::DecorationPixmap border) const
GLTexture *OpenGLPaintRedirector::texture(PaintRedirector::DecorationPixmap border) const
{
return &m_pixmaps[border];
return m_textures[border];
}
void OpenGLPaintRedirector::resize(PaintRedirector::DecorationPixmap border, const QSize &size)
{
if (m_pixmaps[border].size() != size) {
m_pixmaps[border] = QPixmap(size);
if (!m_textures[border] || m_textures[border]->size() != size) {
delete m_textures[border];
m_textures[border] = new GLTexture(size.width(), size.height());
m_textures[border]->setYInverted(true);
}
m_pixmaps[border].fill(Qt::transparent);
}
void OpenGLPaintRedirector::preparePaint(const QPixmap &pending)
{
m_tempImage = pending.toImage();
}
void OpenGLPaintRedirector::paint(PaintRedirector::DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg)
{
QPainter pt(&m_pixmaps[border]);
pt.translate(-r.topLeft());
pt.setCompositionMode(QPainter::CompositionMode_Source);
pt.setClipRegion(reg);
pt.drawPixmap(b.topLeft(), src);
pt.end();
Q_UNUSED(src)
// clip the sub area
const QRect bounding = reg.boundingRect();
m_textures[border]->update(m_tempImage, bounding.topLeft() - r.topLeft(), QRect(bounding.topLeft() - b.topLeft(), bounding.size()));
}
RasterXRenderPaintRedirector::RasterXRenderPaintRedirector(Client *c, QWidget *widget)

View file

@ -40,6 +40,7 @@ namespace KWin
class Client;
class Deleted;
class XRenderPicture;
class GLTexture;
// This class redirects all painting of a given widget (including its children)
// into a paint device (QPixmap).
@ -90,8 +91,8 @@ public slots:
void ensurePixmapsPainted();
protected:
PaintRedirector(Client *c, QWidget* widget);
virtual const QPixmap *pixmap(DecorationPixmap border) const;
virtual xcb_render_picture_t picture(DecorationPixmap border) const;
virtual GLTexture *texture(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size) = 0;
virtual void preparePaint(const QPixmap &pending);
virtual void paint(DecorationPixmap border, const QRect& r, const QRect &b, const QPixmap& src, const QRegion &reg) = 0;
@ -121,12 +122,14 @@ public:
virtual ~OpenGLPaintRedirector();
protected:
virtual const QPixmap *pixmap(DecorationPixmap border) const;
virtual void resize(DecorationPixmap border, const QSize &size);
virtual void paint(DecorationPixmap border, const QRect &r, const QRect &b, const QPixmap &src, const QRegion &reg);
virtual GLTexture *texture(DecorationPixmap border) const;
virtual void preparePaint(const QPixmap &pending);
private:
QPixmap m_pixmaps[PixmapCount];
QImage m_tempImage;
GLTexture* m_textures[PixmapCount];
};
class NativeXRenderPaintRedirector : public PaintRedirector
@ -166,30 +169,30 @@ private:
template <>
inline
const QPixmap *PaintRedirector::bottomDecoPixmap() const
GLTexture *PaintRedirector::bottomDecoPixmap() const
{
return pixmap(BottomPixmap);
return texture(BottomPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::leftDecoPixmap() const
GLTexture *PaintRedirector::leftDecoPixmap() const
{
return pixmap(LeftPixmap);
return texture(LeftPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::rightDecoPixmap() const
GLTexture *PaintRedirector::rightDecoPixmap() const
{
return pixmap(RightPixmap);
return texture(RightPixmap);
}
template <>
inline
const QPixmap *PaintRedirector::topDecoPixmap() const
GLTexture *PaintRedirector::topDecoPixmap() const
{
return pixmap(TopPixmap);
return texture(TopPixmap);
}
template <>

View file

@ -900,20 +900,12 @@ SceneOpenGL::Window::Window(Toplevel* c)
: Scene::Window(c)
, m_scene(NULL)
, texture(NULL)
, topTexture(NULL)
, leftTexture(NULL)
, rightTexture(NULL)
, bottomTexture(NULL)
{
}
SceneOpenGL::Window::~Window()
{
delete texture;
delete topTexture;
delete leftTexture;
delete rightTexture;
delete bottomTexture;
}
// Bind the window pixmap to an OpenGL texture.
@ -950,21 +942,6 @@ void SceneOpenGL::Window::discardTexture()
if (texture) {
texture->discard();
}
if (!Extensions::nonNativePixmaps()) {
// only discard if the deco pixmaps use TFP
if (topTexture) {
topTexture->discard();
}
if (leftTexture) {
leftTexture->discard();
}
if (rightTexture) {
rightTexture->discard();
}
if (bottomTexture) {
bottomTexture->discard();
}
}
}
// This call is used in SceneOpenGL::windowGeometryShapeChanged(),
@ -1112,15 +1089,9 @@ void SceneOpenGL::Window::paintDecorations(const WindowPaintData &data, const QR
}
WindowQuadList decoration = data.quads.select(WindowQuadDecoration);
QRect topRect, leftRect, rightRect, bottomRect;
const bool updateDeco = redirector->requiresRepaint();
t->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative);
const QPixmap *left = redirector->leftDecoPixmap<const QPixmap*>();
const QPixmap *top = redirector->topDecoPixmap<const QPixmap*>();
const QPixmap *right = redirector->rightDecoPixmap<const QPixmap*>();
const QPixmap *bottom = redirector->bottomDecoPixmap<const QPixmap*>();
WindowQuadList topList, leftList, rightList, bottomList;
foreach (const WindowQuad & quad, decoration) {
@ -1143,58 +1114,26 @@ void SceneOpenGL::Window::paintDecorations(const WindowPaintData &data, const QR
}
redirector->ensurePixmapsPainted();
paintDecoration(top, DecorationTop, region, topRect, data, topList, updateDeco, hardwareClipping);
paintDecoration(left, DecorationLeft, region, leftRect, data, leftList, updateDeco, hardwareClipping);
paintDecoration(right, DecorationRight, region, rightRect, data, rightList, updateDeco, hardwareClipping);
paintDecoration(bottom, DecorationBottom, region, bottomRect, data, bottomList, updateDeco, hardwareClipping);
GLTexture *left = redirector->leftDecoPixmap<GLTexture*>();
GLTexture *top = redirector->topDecoPixmap<GLTexture*>();
GLTexture *right = redirector->rightDecoPixmap<GLTexture*>();
GLTexture *bottom = redirector->bottomDecoPixmap<GLTexture*>();
paintDecoration(top, DecorationTop, region, topRect, data, topList, hardwareClipping);
paintDecoration(left, DecorationLeft, region, leftRect, data, leftList, hardwareClipping);
paintDecoration(right, DecorationRight, region, rightRect, data, rightList, hardwareClipping);
paintDecoration(bottom, DecorationBottom, region, bottomRect, data, bottomList, hardwareClipping);
redirector->markAsRepainted();
}
void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType decorationType,
void SceneOpenGL::Window::paintDecoration(GLTexture *decorationTexture, TextureType decorationType,
const QRegion& region, const QRect& rect, const WindowPaintData& data,
const WindowQuadList& quads, bool updateDeco, bool hardwareClipping)
const WindowQuadList& quads, bool hardwareClipping)
{
SceneOpenGL::Texture* decorationTexture;
switch(decorationType) {
case DecorationTop:
if (!topTexture) {
topTexture = m_scene->createTexture();
}
decorationTexture = topTexture;
break;
case DecorationLeft:
if (!leftTexture) {
leftTexture = m_scene->createTexture();
}
decorationTexture = leftTexture;
break;
case DecorationRight:
if (!rightTexture) {
rightTexture = m_scene->createTexture();
}
decorationTexture = rightTexture;
break;
case DecorationBottom:
if (!bottomTexture) {
bottomTexture = m_scene->createTexture();
}
decorationTexture = bottomTexture;
break;
default:
if (!decorationTexture) {
return;
}
if (decoration->isNull() || !decorationTexture) {
return;
}
if (decorationTexture->isNull() || updateDeco) {
bool success = decorationTexture->load(*decoration);
if (!success) {
kDebug(1212) << "Failed to bind decoartion";
return;
}
}
// We have to update the texture although we do not paint anything.
// This is especially needed if we draw the opaque part of the window
@ -1262,7 +1201,7 @@ void SceneOpenGL::Window::paintShadow(const QRegion &region, const WindowPaintDa
#endif
}
void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const
void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, GLTexture *tex) const
{
QVector<float> vertices;
QVector<float> texcoords;
@ -1350,21 +1289,37 @@ void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQu
GLTexture *SceneOpenGL::Window::textureForType(SceneOpenGL::Window::TextureType type)
{
GLTexture *tex = NULL;
PaintRedirector *redirector = NULL;
if (type != Content && type != Shadow) {
if (toplevel->isClient()) {
redirector = static_cast<Client*>(toplevel)->decorationPaintRedirector();
} else if (toplevel->isDeleted()) {
redirector = static_cast<Deleted*>(toplevel)->decorationPaintRedirector();
}
}
switch(type) {
case Content:
tex = texture;
break;
case DecorationTop:
tex = topTexture;
if (redirector) {
tex = redirector->topDecoPixmap<GLTexture*>();
}
break;
case DecorationLeft:
tex = leftTexture;
if (redirector) {
tex = redirector->leftDecoPixmap<GLTexture*>();
}
break;
case DecorationRight:
tex = rightTexture;
if (redirector) {
tex = redirector->rightDecoPixmap<GLTexture*>();
}
break;
case DecorationBottom:
tex = bottomTexture;
if (redirector) {
tex = redirector->bottomDecoPixmap<GLTexture*>();
}
break;
case Shadow:
tex = static_cast<SceneOpenGLShadow*>(m_shadow)->shadowTexture();

View file

@ -216,9 +216,9 @@ protected:
};
QMatrix4x4 transformation(int mask, const WindowPaintData &data) const;
void paintDecoration(const QPixmap* decoration, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool updateDeco, bool hardwareClipping);
void paintDecoration(GLTexture *decorationTexture, TextureType decorationType, const QRegion& region, const QRect& rect, const WindowPaintData& data, const WindowQuadList& quads, bool hardwareClipping);
void paintShadow(const QRegion &region, const WindowPaintData &data, bool hardwareClipping);
void makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const;
void makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, GLTexture *tex) const;
void renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture* tex, bool normalized, bool hardwareClipping);
/**
* @brief Called from performPaint once it is determined whether the window will be painted.
@ -275,10 +275,6 @@ private:
template<class T>
void paintDecorations(const WindowPaintData &data, const QRegion &region, bool hardwareClipping);
Texture *texture;
Texture *topTexture;
Texture *leftTexture;
Texture *rightTexture;
Texture *bottomTexture;
};
class SceneOpenGL2Window : public SceneOpenGL::Window