From aa549f45d52d90dbe640225ff0515a8154062f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 22 Feb 2013 10:13:46 +0100 Subject: [PATCH] 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. --- libkwineffects/kwingltexture.cpp | 50 +++++++++++++- libkwineffects/kwingltexture.h | 1 + paintredirector.cpp | 36 ++++++---- paintredirector.h | 25 +++---- scene_opengl.cpp | 109 +++++++++---------------------- scene_opengl.h | 8 +-- 6 files changed, 122 insertions(+), 107 deletions(-) diff --git a/libkwineffects/kwingltexture.cpp b/libkwineffects/kwingltexture.cpp index c1c3e9409f..77b663a4a3 100644 --- a/libkwineffects/kwingltexture.cpp +++ b/libkwineffects/kwingltexture.cpp @@ -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; diff --git a/libkwineffects/kwingltexture.h b/libkwineffects/kwingltexture.h index a60ae48049..71b8078775 100644 --- a/libkwineffects/kwingltexture.h +++ b/libkwineffects/kwingltexture.h @@ -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(); diff --git a/paintredirector.cpp b/paintredirector.cpp index 3bbe9d0ef0..ee6b41422b 100644 --- a/paintredirector.cpp +++ b/paintredirector.cpp @@ -28,6 +28,7 @@ DEALINGS IN THE SOFTWARE. #include "client.h" #include "deleted.h" #include "effects.h" +#include #include #include #include @@ -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; isize() != 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 ®) { - 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) diff --git a/paintredirector.h b/paintredirector.h index 06279750f4..68d06acb4a 100644 --- a/paintredirector.h +++ b/paintredirector.h @@ -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 ®) = 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 ®); + 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 <> diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 793ffbc54b..ff5acc136c 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -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 *top = redirector->topDecoPixmap(); - const QPixmap *right = redirector->rightDecoPixmap(); - const QPixmap *bottom = redirector->bottomDecoPixmap(); - 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 *top = redirector->topDecoPixmap(); + GLTexture *right = redirector->rightDecoPixmap(); + GLTexture *bottom = redirector->bottomDecoPixmap(); + 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 ®ion, 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 vertices; QVector 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(toplevel)->decorationPaintRedirector(); + } else if (toplevel->isDeleted()) { + redirector = static_cast(toplevel)->decorationPaintRedirector(); + } + } switch(type) { case Content: tex = texture; break; case DecorationTop: - tex = topTexture; + if (redirector) { + tex = redirector->topDecoPixmap(); + } break; case DecorationLeft: - tex = leftTexture; + if (redirector) { + tex = redirector->leftDecoPixmap(); + } break; case DecorationRight: - tex = rightTexture; + if (redirector) { + tex = redirector->rightDecoPixmap(); + } break; case DecorationBottom: - tex = bottomTexture; + if (redirector) { + tex = redirector->bottomDecoPixmap(); + } break; case Shadow: tex = static_cast(m_shadow)->shadowTexture(); diff --git a/scene_opengl.h b/scene_opengl.h index cd3dd86952..9341287aa1 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -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 ®ion, 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 void paintDecorations(const WindowPaintData &data, const QRegion ®ion, bool hardwareClipping); Texture *texture; - Texture *topTexture; - Texture *leftTexture; - Texture *rightTexture; - Texture *bottomTexture; }; class SceneOpenGL2Window : public SceneOpenGL::Window