From b837a3fca184fce0c7da317b2866d409892e71eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 23 Jun 2011 19:06:12 +0200 Subject: [PATCH] Render Shadow with only one GL texture Copies the shadow parts into one image and creates a GLTexture from the image, so that we can render the complete shadow with just one texture and one painting pass. Should remove most of the overhead involved when rendering the new Shadows. As a side effect this should fix missing shadows with non-NPOT GPUs and a rendering glitch reported with NVIDIA. REVIEW: 101742 --- libkwineffects/kwinglutils.cpp | 5 + libkwineffects/kwinglutils.h | 4 + scene_opengl.cpp | 242 +++++++++++++++++++++++---------- scene_opengl.h | 28 ++-- scene_xrender.h | 3 + shadow.cpp | 3 + shadow.h | 15 +- 7 files changed, 211 insertions(+), 89 deletions(-) diff --git a/libkwineffects/kwinglutils.cpp b/libkwineffects/kwinglutils.cpp index 15597d48dd..9875d52f81 100644 --- a/libkwineffects/kwinglutils.cpp +++ b/libkwineffects/kwinglutils.cpp @@ -695,6 +695,11 @@ QImage GLTexture::convertToGLFormat(const QImage& img) const return res; } +bool GLTexture::isYInverted() const +{ + return y_inverted; +} + //**************************************** // GLShader //**************************************** diff --git a/libkwineffects/kwinglutils.h b/libkwineffects/kwinglutils.h index 2773fd0f2b..34ba33d0c6 100644 --- a/libkwineffects/kwinglutils.h +++ b/libkwineffects/kwinglutils.h @@ -150,6 +150,10 @@ public: int height() const { return mSize.height(); /// @since 4.5 } + /** + * @since 4.7 + **/ + bool isYInverted() const; virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D); virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D); diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 2d472f3504..07e0c45398 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -513,14 +513,7 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData // shadow if (m_shadow) { - paintShadow(WindowQuadShadowTop, region, data); - paintShadow(WindowQuadShadowTopRight, region, data); - paintShadow(WindowQuadShadowRight, region, data); - paintShadow(WindowQuadShadowBottomRight, region, data); - paintShadow(WindowQuadShadowBottom, region, data); - paintShadow(WindowQuadShadowBottomLeft, region, data); - paintShadow(WindowQuadShadowLeft, region, data); - paintShadow(WindowQuadShadowTopLeft, region, data); + paintShadow(region, data); } // decorations Client *client = dynamic_cast(toplevel); @@ -659,10 +652,17 @@ void SceneOpenGL::Window::paintDecoration(const QPixmap* decoration, TextureType #endif } -void SceneOpenGL::Window::paintShadow(WindowQuadType type, const QRegion ®ion, const WindowPaintData &data) +void SceneOpenGL::Window::paintShadow(const QRegion ®ion, const WindowPaintData &data) { - WindowQuadList quads = data.quads.select(type); - Texture *texture = static_cast(m_shadow)->textureForQuadType(type); + WindowQuadList quads = data.quads.select(WindowQuadShadowTopLeft); + quads.append(data.quads.select(WindowQuadShadowTop)); + quads.append(data.quads.select(WindowQuadShadowTopRight)); + quads.append(data.quads.select(WindowQuadShadowRight)); + quads.append(data.quads.select(WindowQuadShadowBottomRight)); + quads.append(data.quads.select(WindowQuadShadowBottom)); + quads.append(data.quads.select(WindowQuadShadowBottomLeft)); + quads.append(data.quads.select(WindowQuadShadowLeft)); + GLTexture *texture = static_cast(m_shadow)->shadowTexture(); if (!texture) { return; } @@ -744,7 +744,7 @@ void SceneOpenGL::Window::makeDecorationArrays(const WindowQuadList& quads, cons GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices.data(), texcoords.data()); } -void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQuadList& quads, Texture *tex, bool normalized) +void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture *tex, bool normalized) { if (quads.isEmpty()) return; @@ -762,7 +762,7 @@ void SceneOpenGL::Window::renderQuads(int, const QRegion& region, const WindowQu size.setHeight(1.0); } #endif - quads.makeArrays(&vertices, &texcoords, size, tex->getYInverted()); + quads.makeArrays(&vertices, &texcoords, size, tex->isYInverted()); GLVertexBuffer::streamingBuffer()->setData(quads.count() * 6, 2, vertices, texcoords); GLVertexBuffer::streamingBuffer()->render(region, GL_TRIANGLES); delete[] vertices; @@ -798,7 +798,7 @@ void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double } } -void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture) +void SceneOpenGL::Window::prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) { if (shader) { prepareShaderRenderStates(type, opacity, brightness, saturation, shader); @@ -835,7 +835,7 @@ void SceneOpenGL::Window::prepareShaderRenderStates(TextureType type, double opa shader->setUniform(GLShader::AlphaToOne, opaque ? 1 : 0); } -void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex) +void SceneOpenGL::Window::prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) { #ifdef KWIN_HAVE_OPENGLES Q_UNUSED(type) @@ -1000,7 +1000,7 @@ void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double } } -void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture) +void SceneOpenGL::Window::restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture) { if (shader) { restoreShaderRenderStates(type, opacity, brightness, saturation, shader); @@ -1026,7 +1026,7 @@ void SceneOpenGL::Window::restoreShaderRenderStates(TextureType type, double opa #endif } -void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex) +void SceneOpenGL::Window::restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex) { Q_UNUSED(type) #ifdef KWIN_HAVE_OPENGLES @@ -1535,71 +1535,169 @@ void SceneOpenGL::EffectFrame::cleanup() //**************************************** SceneOpenGLShadow::SceneOpenGLShadow(Toplevel *toplevel) : Shadow(toplevel) + , m_texture(NULL) { } SceneOpenGLShadow::~SceneOpenGLShadow() { - for (int i=0; i topLevel()->width()) || + (rightRect.width() - rightOffset() > topLevel()->width()) || + (topRect.height() - topOffset() > topLevel()->height()) || + (bottomRect.height() - bottomOffset() > topLevel()->height())) { + // if our shadow is bigger than the window, we don't render the shadow + setShadowRegion(QRegion()); + return; } - if (texture) { - if (texture->texture() != None) { - glBindTexture(texture->target(), texture->texture()); - } else if (!pixmap.isNull()) { - const bool success = texture->load(pixmap); - if (!success) { - kDebug(1212) << "Failed to bind shadow pixmap"; - return NULL; - } - } else { - return NULL; - } + // calculate the width + const qreal cornerWidth = topLeftRect.width() + topRightRect.width() + bottomLeftRect.width() + bottomRightRect.width(); + const qreal leftRightWidth = leftRect.width() + rightRect.width(); + const qreal topBottomWidth = topRect.width() + bottomRect.width(); + // calculate the height + const qreal cornerHeight = qMax(topLeftRect.height(), qMax(topRightRect.height(), qMax(bottomLeftRect.height(), bottomRightRect.height()))); + const qreal leftRightHeight = qMax(leftRect.height(), rightRect.height()); + const qreal width = m_texture->width(); + const qreal height = m_texture->height(); + qreal tx1, tx2, ty1, ty2; + tx1 = tx2 = ty1 = ty2 = 0.0; + tx2 = topLeftRect.width()/width; + ty2 = topLeftRect.height()/height; + WindowQuad topLeftQuad(WindowQuadShadowTopLeft); + topLeftQuad[ 0 ] = WindowVertex(-leftOffset(), -topOffset(), tx1, ty1); + topLeftQuad[ 1 ] = WindowVertex(-leftOffset() + topLeftRect.width(), -topOffset(), tx2, ty1); + topLeftQuad[ 2 ] = WindowVertex(-leftOffset() + topLeftRect.width(), -topOffset() + topLeftRect.height(), tx2, ty2); + topLeftQuad[ 3 ] = WindowVertex(-leftOffset(), -topOffset() + topLeftRect.height(), tx1, ty2); + quads.append(topLeftQuad); + tx2 = topRect.width()/width; + ty1 = (cornerHeight + leftRightHeight)/height; + ty2 = (cornerHeight + leftRightHeight + topRect.height())/height; + WindowQuad topQuad(WindowQuadShadowTop); + topQuad[ 0 ] = WindowVertex(-leftOffset() + topLeftRect.width(), -topOffset(), tx1, ty1); + topQuad[ 1 ] = WindowVertex(topLevel()->width() + rightOffset() - topRightRect.width(), -topOffset(), tx2, ty1); + topQuad[ 2 ] = WindowVertex(topLevel()->width() + rightOffset() - topRightRect.width(), -topOffset() + topRect.height(),tx2, ty2); + topQuad[ 3 ] = WindowVertex(-leftOffset() + topLeftRect.width(), -topOffset() + topRect.height(), tx1, ty2); + quads.append(topQuad); + tx1 = topLeftRect.width()/width; + tx2 = (topLeftRect.width() + topRightRect.width())/width; + ty1 = 0.0; + ty2 = topRightRect.height()/height; + WindowQuad topRightQuad(WindowQuadShadowTopRight); + topRightQuad[ 0 ] = WindowVertex(topLevel()->width() + rightOffset() - topRightRect.width(), -topOffset(), tx1, ty1); + topRightQuad[ 1 ] = WindowVertex(topLevel()->width() + rightOffset(), -topOffset(), tx2, ty1); + topRightQuad[ 2 ] = WindowVertex(topLevel()->width() + rightOffset(), -topOffset() + topRightRect.height(), tx2, ty2); + topRightQuad[ 3 ] = WindowVertex(topLevel()->width() + rightOffset() - topRightRect.width(), -topOffset() + topRightRect.height(), tx1, ty2); + quads.append(topRightQuad); + tx1 = leftRect.width()/width; + tx2 = leftRightWidth/width; + ty1 = cornerHeight/height; + ty2 = (cornerHeight+rightRect.height())/height; + WindowQuad rightQuad(WindowQuadShadowRight); + rightQuad[ 0 ] = WindowVertex(topLevel()->width() + rightOffset() - rightRect.width(), -topOffset() + topRightRect.height(), tx1, ty1); + rightQuad[ 1 ] = WindowVertex(topLevel()->width() + rightOffset(), -topOffset() + topRightRect.height(), tx2, ty1); + rightQuad[ 2 ] = WindowVertex(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset() - bottomRightRect.height(), tx2, ty2); + rightQuad[ 3 ] = WindowVertex(topLevel()->width() + rightOffset() - rightRect.width(), topLevel()->height() + bottomOffset() - bottomRightRect.height(), tx1, ty2); + quads.append(rightQuad); + tx1 = (topLeftRect.width() + topRightRect.width() + bottomLeftRect.width())/width; + tx2 = cornerWidth/width; + ty1 = 0.0; + ty2 = bottomRightRect.height()/height; + WindowQuad bottomRightQuad(WindowQuadShadowBottomRight); + bottomRightQuad[ 0 ] = WindowVertex(topLevel()->width() + rightOffset() - bottomRightRect.width(), topLevel()->height() + bottomOffset() - bottomRightRect.height(), tx1, ty1); + bottomRightQuad[ 1 ] = WindowVertex(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset() - bottomRightRect.height(), tx2, ty1); + bottomRightQuad[ 2 ] = WindowVertex(topLevel()->width() + rightOffset(), topLevel()->height() + bottomOffset(), tx2, ty2); + bottomRightQuad[ 3 ] = WindowVertex(topLevel()->width() + rightOffset() - bottomRightRect.width(), topLevel()->height() + bottomOffset(), tx1, ty2); + quads.append(bottomRightQuad); + tx1 = topRect.width()/width; + tx2 = topBottomWidth/width; + ty1 = (cornerHeight + leftRightHeight)/height; + ty2 = (cornerHeight + leftRightHeight + bottomRect.height())/height; + WindowQuad bottomQuad(WindowQuadShadowBottom); + bottomQuad[ 0 ] = WindowVertex(-leftOffset() + bottomLeftRect.width(), topLevel()->height() + bottomOffset() - bottomRect.height(), tx1, ty1); + bottomQuad[ 1 ] = WindowVertex(topLevel()->width() + rightOffset() - bottomRightRect.width(), topLevel()->height() + bottomOffset() - bottomRect.height(), tx2, ty1); + bottomQuad[ 2 ] = WindowVertex(topLevel()->width() + rightOffset() - bottomRightRect.width(), topLevel()->height() + bottomOffset(), tx2, ty2); + bottomQuad[ 3 ] = WindowVertex(-leftOffset() + bottomLeftRect.width(), topLevel()->height() + bottomOffset(), tx1, ty2); + quads.append(bottomQuad); + tx1 = (topLeftRect.width() + topRightRect.width())/width; + tx2 = (topLeftRect.width() + topRightRect.width() + bottomLeftRect.width())/width; + ty1 = 0.0; + ty2 = bottomLeftRect.height()/height; + WindowQuad bottomLeftQuad(WindowQuadShadowBottomLeft); + bottomLeftQuad[ 0 ] = WindowVertex(-leftOffset(), topLevel()->height() + bottomOffset() - bottomLeftRect.height(), tx1, ty1); + bottomLeftQuad[ 1 ] = WindowVertex(-leftOffset() + bottomLeftRect.width(), topLevel()->height() + bottomOffset() - bottomLeftRect.height(), tx2, ty1); + bottomLeftQuad[ 2 ] = WindowVertex(-leftOffset() + bottomLeftRect.width(), topLevel()->height() + bottomOffset(), tx2, ty2); + bottomLeftQuad[ 3 ] = WindowVertex(-leftOffset(), topLevel()->height() + bottomOffset(), tx1, ty2); + quads.append(bottomLeftQuad); + tx1 = 0.0; + tx2 = leftRect.width()/width; + ty1 = cornerHeight/height; + ty2 = (cornerHeight+leftRect.height())/height; + WindowQuad leftQuad(WindowQuadShadowLeft); + leftQuad[ 0 ] = WindowVertex(-leftOffset(), -topOffset() + topLeftRect.height(), tx1, ty1); + leftQuad[ 1 ] = WindowVertex(-leftOffset() + leftRect.width(), -topOffset() + topLeftRect.height(), tx2, ty1); + leftQuad[ 2 ] = WindowVertex(-leftOffset() + leftRect.width(), topLevel()->height() + bottomOffset() - bottomLeftRect.height(), tx2, ty2); + leftQuad[ 3 ] = WindowVertex(-leftOffset(), topLevel()->height() + bottomOffset() - bottomLeftRect.height(), tx1, ty2); + quads.append(leftQuad); + m_shadowQuads = quads; +} + +bool SceneOpenGLShadow::prepareBackend() +{ + const QRect topRect(QPoint(0, 0), shadowPixmap(ShadowElementTop).size()); + const QRect topRightRect(QPoint(0, 0), shadowPixmap(ShadowElementTopRight).size()); + const QRect rightRect(QPoint(0, 0), shadowPixmap(ShadowElementRight).size()); + const QRect bottomRightRect(QPoint(0, 0), shadowPixmap(ShadowElementBottomRight).size()); + const QRect bottomRect(QPoint(0, 0), shadowPixmap(ShadowElementBottom).size()); + const QRect bottomLeftRect(QPoint(0, 0), shadowPixmap(ShadowElementBottomLeft).size()); + const QRect leftRect(QPoint(0, 0), shadowPixmap(ShadowElementLeft).size()); + const QRect topLeftRect(QPoint(0, 0), shadowPixmap(ShadowElementTopLeft).size()); + // calculate the width + const int cornerWidth = topLeftRect.width() + topRightRect.width() + bottomLeftRect.width() + bottomRightRect.width(); + const int leftRightWidth = leftRect.width() + rightRect.width(); + const int topBottomWidth = topRect.width() + bottomRect.width(); + const int width = qMax(cornerWidth, qMax(leftRightWidth, topBottomWidth)); + // calculate the height + const int cornerHeight = qMax(topLeftRect.height(), qMax(topRightRect.height(), qMax(bottomLeftRect.height(), bottomRightRect.height()))); + const int leftRightHeight = qMax(leftRect.height(), rightRect.height()); + const int topBottomHeight = qMax(topRect.height(), bottomRect.height()); + const int height = cornerHeight + leftRightHeight + topBottomHeight; + + QImage image(width, height, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter p; + p.begin(&image); + p.drawPixmap(0, 0, shadowPixmap(ShadowElementTopLeft)); + p.drawPixmap(topLeftRect.width(), 0, shadowPixmap(ShadowElementTopRight)); + p.drawPixmap(topLeftRect.width() + topRightRect.width(), 0, shadowPixmap(ShadowElementBottomLeft)); + p.drawPixmap(topLeftRect.width() + topRightRect.width() + bottomLeftRect.width(), 0, shadowPixmap(ShadowElementBottomRight)); + p.drawPixmap(0, cornerHeight, shadowPixmap(ShadowElementLeft)); + p.drawPixmap(leftRect.width(), cornerHeight, shadowPixmap(ShadowElementRight)); + p.drawPixmap(0, cornerHeight + leftRightHeight, shadowPixmap(ShadowElementTop)); + p.drawPixmap(topRect.width(), cornerHeight + leftRightHeight, shadowPixmap(ShadowElementBottom)); + p.end(); + + if (m_texture) { + delete m_texture; + m_texture = NULL; } - return texture; + + m_texture = new GLTexture(image); + return true; } } // namespace diff --git a/scene_opengl.h b/scene_opengl.h index 0de22ce23e..fdff2f0340 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -167,16 +167,16 @@ 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); - void paintShadow(WindowQuadType type, const QRegion ®ion, const WindowPaintData &data); + void paintShadow(const QRegion ®ion, const WindowPaintData &data); void makeDecorationArrays(const WindowQuadList& quads, const QRect &rect, Texture *tex) const; - void renderQuads(int, const QRegion& region, const WindowQuadList& quads, Texture* tex, bool normalized = false); + void renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture* tex, bool normalized = false); void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); - void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture); - void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex); + void prepareStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture); + void prepareRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex); void prepareShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); - void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, Texture *texture); - void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, Texture *tex); + void restoreStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader, GLTexture *texture); + void restoreRenderStates(TextureType type, double opacity, double brightness, double saturation, GLTexture *tex); void restoreShaderRenderStates(TextureType type, double opacity, double brightness, double saturation, GLShader* shader); private: @@ -237,16 +237,14 @@ public: SceneOpenGLShadow(Toplevel *toplevel); virtual ~SceneOpenGLShadow(); - /** - * Returns the Texture for a specific ShadowQuad. The method takes care of performing - * the Texture from Pixmap operation. The calling method can use the returned Texture - * directly. - * In error case the method returns @c NULL. - * @return OpenGL Texture for the Shadow Quad. May be @c NULL. - **/ - SceneOpenGL::Texture *textureForQuadType(WindowQuadType type); + GLTexture *shadowTexture() { + return m_texture; + } +protected: + virtual void buildQuads(); + virtual bool prepareBackend(); private: - SceneOpenGL::Texture m_shadowTextures[ShadowElementsCount]; + GLTexture *m_texture; }; } // namespace diff --git a/scene_xrender.h b/scene_xrender.h index 0600550386..b00a454897 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -165,6 +165,9 @@ public: protected: virtual void buildQuads(); + virtual bool prepareBackend() { + return true; + }; private: QPixmap m_resizedElements[ShadowElementsCount]; diff --git a/shadow.cpp b/shadow.cpp index d51a0259dc..c47b5e9ed7 100644 --- a/shadow.cpp +++ b/shadow.cpp @@ -110,6 +110,9 @@ bool Shadow::init(const QVector< long > &data) m_bottomOffset = data[ShadowElementsCount+2]; m_leftOffset = data[ShadowElementsCount+3]; updateShadowRegion(); + if (!prepareBackend()) { + return false; + } buildQuads(); return true; } diff --git a/shadow.h b/shadow.h index 7127db95ae..43108184b5 100644 --- a/shadow.h +++ b/shadow.h @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include #include +#include namespace KWin { @@ -58,7 +59,10 @@ public: /** * @return Cached Shadow Quads **/ - const WindowQuadList &shadowQuads() { + const WindowQuadList &shadowQuads() const { + return m_shadowQuads; + }; + WindowQuadList &shadowQuads() { return m_shadowQuads; }; @@ -125,6 +129,14 @@ protected: }; virtual void buildQuads(); void updateShadowRegion(); + Toplevel *topLevel() { + return m_topLevel; + }; + void setShadowRegion(const QRegion ®ion) { + m_shadowRegion = region; + }; + virtual bool prepareBackend() = 0; + WindowQuadList m_shadowQuads; private: static QVector readX11ShadowProperty(WId id); @@ -139,7 +151,6 @@ private: int m_leftOffset; // caches QRegion m_shadowRegion; - WindowQuadList m_shadowQuads; QSize m_cachedSize; };