From 40d6bd66d417ab873cb9efc3f527fece844fa0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B6glund?= Date: Sun, 17 Mar 2013 13:34:26 +0100 Subject: [PATCH] kwin: Refactor SceneOpenGL::Window Reimplement performPaint() in SceneOpenGL1Window and SceneOpenGL2Window. The roles between begin/endRenderWindow() and performPaint() are now reversed; performPaint() contains the specialized code while begin/ endRenderWindow() contains the shared code. This reduces the state churn in the OpenGL2 backend from the repeated calls to prepare/restoreStates(). --- scene_opengl.cpp | 210 ++++++++++++++++++++++++++++++++++++----------- scene_opengl.h | 39 +++------ 2 files changed, 175 insertions(+), 74 deletions(-) diff --git a/scene_opengl.cpp b/scene_opengl.cpp index c132ebfdcc..5fe456b1b8 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -1053,11 +1053,10 @@ QMatrix4x4 SceneOpenGL::Window::transformation(int mask, const WindowPaintData & return matrix; } -// paint the window -void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData data) +bool SceneOpenGL::Window::beginRenderWindow(int mask, const QRegion ®ion, WindowPaintData &data) { if (region.isEmpty()) - return; + return false; m_hardwareClipping = region != infiniteRegion() && (mask & PAINT_WINDOW_TRANSFORMED) && !(mask & PAINT_SCREEN_TRANSFORMED); if (region != infiniteRegion() && !m_hardwareClipping) { @@ -1084,7 +1083,7 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData } if (!bindTexture()) { - return; + return false; } if (m_hardwareClipping) { @@ -1100,8 +1099,6 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData m_texture->setFilter(filter == ImageFilterGood ? GL_LINEAR : GL_NEAREST); - beginRenderWindow(mask, data); - const GLVertexAttrib attribs[] = { { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) }, { VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) }, @@ -1111,39 +1108,14 @@ void SceneOpenGL::Window::performPaint(int mask, QRegion region, WindowPaintData vbo->reset(); vbo->setAttribLayout(attribs, 2, sizeof(GLVertex2D)); - // shadow - if (m_shadow) { - paintShadow(region, data); - } - // decorations - if (toplevel->isClient()) { - paintDecorations(data, region); - } else if (toplevel->isDeleted()) { - paintDecorations(data, region); - } - - // paint the content - WindowQuadList contentQuads = data.quads.select(WindowQuadContents); - if (!contentQuads.empty()) { - m_texture->bind(); - prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.screen()); - renderQuads(mask, region, contentQuads, m_texture, false); - restoreStates(Content, data.opacity(), data.brightness(), data.saturation()); - m_texture->unbind(); -#ifndef KWIN_HAVE_OPENGLES - if (m_scene && m_scene->debug()) { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - renderQuads(mask, region, contentQuads, m_texture, false); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } -#endif - } + return true; +} +void SceneOpenGL::Window::endRenderWindow() +{ if (m_hardwareClipping) { glDisable(GL_SCISSOR_TEST); } - - endRenderWindow(data); } @@ -1357,11 +1329,31 @@ SceneOpenGL2Window::~SceneOpenGL2Window() { } -void SceneOpenGL2Window::beginRenderWindow(int mask, const WindowPaintData &data) +QVector4D SceneOpenGL2Window::modulate(float opacity, float brightness) const { + const float a = opacity; + const float rgb = opacity * brightness; + + return QVector4D(rgb, rgb, rgb, a); +} + +void SceneOpenGL2Window::setBlendEnabled(bool enabled) +{ + if (enabled && !m_blendingEnabled) + glEnable(GL_BLEND); + else if (!enabled && m_blendingEnabled) + glDisable(GL_BLEND); + + m_blendingEnabled = enabled; +} + +void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData data) +{ + if (!beginRenderWindow(mask, region, data)) + return; + GLShader *shader = data.shader; if (!shader) { - // set the shader for uniform initialising in paint decoration if ((mask & Scene::PAINT_WINDOW_TRANSFORMED) || (mask & Scene::PAINT_SCREEN_TRANSFORMED)) { shader = ShaderManager::instance()->pushShader(ShaderManager::GenericShader); } else { @@ -1370,14 +1362,111 @@ void SceneOpenGL2Window::beginRenderWindow(int mask, const WindowPaintData &data } } + static_cast(m_scene)->colorCorrection()->setupForOutput(data.screen()); + shader->setUniform(GLShader::WindowTransformation, transformation(mask, data)); -} + shader->setUniform(GLShader::Saturation, data.saturation()); -void SceneOpenGL2Window::endRenderWindow(const WindowPaintData &data) -{ - if (!data.shader) { - ShaderManager::instance()->popShader(); + const GLenum filter = (mask & (Effect::PAINT_WINDOW_TRANSFORMED | Effect::PAINT_SCREEN_TRANSFORMED)) + && options->glSmoothScale() != 0 ? GL_LINEAR : GL_NEAREST; + + WindowQuadList contentQuads; + WindowQuadList shadowQuads; + WindowQuadList decoQuads[2]; + + // Split the quads into separate lists for each type + foreach (const WindowQuad &quad, data.quads) { + switch (quad.type()) { + case WindowQuadDecorationLeftRight: + decoQuads[0].append(quad); + continue; + + case WindowQuadDecorationTopBottom: + decoQuads[1].append(quad); + continue; + + case WindowQuadContents: + contentQuads.append(quad); + continue; + + case WindowQuadShadowTopLeft: + case WindowQuadShadowTop: + case WindowQuadShadowTopRight: + case WindowQuadShadowLeft: + case WindowQuadShadowRight: + case WindowQuadShadowBottomLeft: + case WindowQuadShadowBottom: + case WindowQuadShadowBottomRight: + shadowQuads.append(quad); + continue; + + default: + continue; + } + } + + // Make sure the blend function is set up correctly in case we will be doing blending + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + + // Shadow + // ----------- + if (m_shadow && !shadowQuads.isEmpty()) { + if (GLTexture *texture = static_cast(m_shadow)->shadowTexture()) { + setBlendEnabled(true); + + shader->setUniform(GLShader::ModulationConstant, + modulate(data.opacity() * data.decorationOpacity(), data.brightness())); + + texture->setFilter(filter); + texture->setWrapMode(GL_CLAMP_TO_EDGE); + texture->bind(); + + renderQuads(0, region, shadowQuads, texture, true); + } + } + + + // Decorations + // ----------- + GLTexture *textures[2]; + + if (!(decoQuads[0].isEmpty() && decoQuads[1].isEmpty()) && getDecorationTextures(textures)) { + setBlendEnabled(true); + + shader->setUniform(GLShader::ModulationConstant, + modulate(data.opacity() * data.decorationOpacity(), data.brightness())); + + for (int i = 0; i < 2; i++) { + if (!textures[i] || decoQuads[i].isEmpty()) + continue; + + textures[i]->setFilter(filter); + textures[i]->setWrapMode(GL_CLAMP_TO_EDGE); + textures[i]->bind(); + + renderQuads(0, region, decoQuads[i], textures[i], false); + } } + + + // Content + // --------- + if (!contentQuads.isEmpty()) { + setBlendEnabled(!isOpaque() || data.opacity() < 1.0); + + shader->setUniform(GLShader::ModulationConstant, modulate(data.opacity(), data.brightness())); + + m_texture->bind(); + renderQuads(mask, region, contentQuads, m_texture, false); + } + + setBlendEnabled(false); + + if (!data.shader) + ShaderManager::instance()->popShader(); + + endRenderWindow(); } void SceneOpenGL2Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) @@ -1444,15 +1533,42 @@ SceneOpenGL1Window::~SceneOpenGL1Window() { } -void SceneOpenGL1Window::beginRenderWindow(int mask, const WindowPaintData &data) +// paint the window +void SceneOpenGL1Window::performPaint(int mask, QRegion region, WindowPaintData data) { + if (!beginRenderWindow(mask, region, data)) + return; + pushMatrix(transformation(mask, data)); -} -void SceneOpenGL1Window::endRenderWindow(const WindowPaintData &data) -{ - Q_UNUSED(data) + // shadow + if (m_shadow) { + paintShadow(region, data); + } + // decorations + paintDecorations(data, region); + + // paint the content + WindowQuadList contentQuads = data.quads.select(WindowQuadContents); + if (!contentQuads.empty()) { + m_texture->bind(); + prepareStates(Content, data.opacity(), data.brightness(), data.saturation(), data.screen()); + renderQuads(mask, region, contentQuads, m_texture, false); + restoreStates(Content, data.opacity(), data.brightness(), data.saturation()); + m_texture->unbind(); + +#ifndef KWIN_HAVE_OPENGLES + if (m_scene && m_scene->debug()) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + renderQuads(mask, region, contentQuads, m_texture, false); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +#endif + } + popMatrix(); + + endRenderWindow(); } void SceneOpenGL1Window::prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen) diff --git a/scene_opengl.h b/scene_opengl.h index 0e65e9735e..f5cff24f54 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -33,6 +33,7 @@ namespace KWin class ColorCorrection; class LanczosFilter; class OpenGLBackend; +class OpenGLPaintRedirector; class SceneOpenGL : public Scene @@ -214,7 +215,9 @@ class SceneOpenGL::Window { public: virtual ~Window(); - virtual void performPaint(int mask, QRegion region, WindowPaintData data); + bool beginRenderWindow(int mask, const QRegion ®ion, WindowPaintData &data); + virtual void performPaint(int mask, QRegion region, WindowPaintData data) = 0; + void endRenderWindow(); virtual void pixmapDiscarded(); bool bindTexture(); void discardTexture(); @@ -233,28 +236,10 @@ protected: }; QMatrix4x4 transformation(int mask, const WindowPaintData &data) const; + bool getDecorationTextures(GLTexture **textures) const; void paintDecoration(GLTexture *texture, TextureType type, const QRegion ®ion, const WindowPaintData &data, const WindowQuadList &quads); void paintShadow(const QRegion ®ion, const WindowPaintData &data); void renderQuads(int, const QRegion& region, const WindowQuadList& quads, GLTexture* tex, bool normalized); - /** - * @brief Called from performPaint once it is determined whether the window will be painted. - * This method has to be implemented by the concrete sub class to perform operations for setting - * up the OpenGL state (e.g. pushing a matrix). - * - * @param mask The mask which is used to render the Window - * @param data The WindowPaintData for this frame - * @see performPaint - * @see endRenderWindow - **/ - virtual void beginRenderWindow(int mask, const WindowPaintData &data) = 0; - /** - * @brief Called from performPaint once the window and decoration has been rendered. - * This method has to be implemented by the concrete sub class to perform operations for resetting - * the OpenGL state after rendering this window (e.g. pop matrix). - * - * @param data The WindowPaintData with which this window got rendered - **/ - virtual void endRenderWindow(const WindowPaintData &data) = 0; /** * @brief Prepare the OpenGL rendering state before the texture with @p type will be rendered. * @@ -284,15 +269,15 @@ protected: **/ GLTexture *textureForType(TextureType type); + void paintDecorations(const WindowPaintData &data, const QRegion ®ion); + protected: SceneOpenGL *m_scene; bool m_hardwareClipping; + Texture *m_texture; private: OpenGLPaintRedirector *paintRedirector() const; - bool getDecorationTextures(GLTexture **textures) const; - void paintDecorations(const WindowPaintData &data, const QRegion ®ion); - Texture *m_texture; }; class SceneOpenGL2Window : public SceneOpenGL::Window @@ -302,8 +287,9 @@ public: virtual ~SceneOpenGL2Window(); protected: - virtual void beginRenderWindow(int mask, const WindowPaintData &data); - virtual void endRenderWindow(const WindowPaintData &data); + QVector4D modulate(float opacity, float brightness) const; + void setBlendEnabled(bool enabled); + virtual void performPaint(int mask, QRegion region, WindowPaintData data); virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen); virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); @@ -322,8 +308,7 @@ public: virtual ~SceneOpenGL1Window(); protected: - virtual void beginRenderWindow(int mask, const WindowPaintData &data); - virtual void endRenderWindow(const WindowPaintData &data); + virtual void performPaint(int mask, QRegion region, WindowPaintData data); virtual void prepareStates(TextureType type, qreal opacity, qreal brightness, qreal saturation, int screen); virtual void restoreStates(TextureType type, qreal opacity, qreal brightness, qreal saturation); };