From 3125333f766c222d350941d0031d6ae6bf02c4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 9 Jul 2012 19:04:56 +0200 Subject: [PATCH] Fix logout effect including port to OpenGL 2 The logout effect was rather broken. First of all it was excluded from build if OpenGL ES is present at build time. The reason for this is that the effect did not work with GLES. In order to fix that the vignetting is ported over to OpenGL 2 by using a dedicated shader. As well the lod based blur is added through a dedicated shader and uses framebuffer blit to get the current rendered buffer before rendering the logout window into a texture. Last but not least the isActive method was broken and is fixed by checking whether the logout window is around. BUG: 303096 FIXED-IN: 4.9.0 REVIEW: 105459 --- effects/CMakeLists.txt | 3 - effects/logout/CMakeLists.txt | 6 + effects/logout/data/logout-blur.frag | 9 ++ effects/logout/data/vignetting.frag | 8 ++ effects/logout/logout.cpp | 185 ++++++++++++++++++--------- effects/logout/logout.h | 5 + 6 files changed, 155 insertions(+), 61 deletions(-) create mode 100644 effects/logout/data/logout-blur.frag create mode 100644 effects/logout/data/vignetting.frag diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 29accb19e4..f5a2269f10 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -118,10 +118,7 @@ if( NOT KWIN_MOBILE_EFFECTS ) include( thumbnailaside/CMakeLists.txt ) include( windowgeometry/CMakeLists.txt ) include( zoom/CMakeLists.txt ) - - if( NOT OPENGLES_FOUND ) include( logout/CMakeLists.txt ) - endif( NOT OPENGLES_FOUND ) endif( NOT KWIN_MOBILE_EFFECTS ) # OpenGL-specific effects diff --git a/effects/logout/CMakeLists.txt b/effects/logout/CMakeLists.txt index 67ccff4004..3a010921c0 100644 --- a/effects/logout/CMakeLists.txt +++ b/effects/logout/CMakeLists.txt @@ -10,3 +10,9 @@ set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} install( FILES logout/logout.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) + +# Data files +install( FILES + logout/data/vignetting.frag + logout/data/logout-blur.frag + DESTINATION ${DATA_INSTALL_DIR}/kwin ) diff --git a/effects/logout/data/logout-blur.frag b/effects/logout/data/logout-blur.frag new file mode 100644 index 0000000000..0d7cf70afe --- /dev/null +++ b/effects/logout/data/logout-blur.frag @@ -0,0 +1,9 @@ +uniform sampler2D sampler; +uniform float u_alphaProgress; + +varying vec2 varyingTexCoords; + +void main() { + gl_FragColor = texture2D(sampler, varyingTexCoords, 1.75); + gl_FragColor.a = u_alphaProgress; +} diff --git a/effects/logout/data/vignetting.frag b/effects/logout/data/vignetting.frag new file mode 100644 index 0000000000..5d034a845d --- /dev/null +++ b/effects/logout/data/vignetting.frag @@ -0,0 +1,8 @@ +uniform vec2 u_center; +uniform float u_radius; +uniform float u_progress; + +void main() { + float d = smoothstep(0, u_radius, distance(gl_FragCoord.xy, u_center)); + gl_FragColor = vec4(0.0, 0.0, 0.0, d * u_progress); +} diff --git a/effects/logout/logout.cpp b/effects/logout/logout.cpp index d6542e6577..1d68f2fc44 100644 --- a/effects/logout/logout.cpp +++ b/effects/logout/logout.cpp @@ -27,6 +27,10 @@ along with this program. If not, see . #include #include #include +#include + +#include +#include namespace KWin { @@ -41,6 +45,8 @@ LogoutEffect::LogoutEffect() , logoutWindowPassed(false) , canDoPersistent(false) , ignoredWindows() + , m_vignettingShader(NULL) + , m_blurShader(NULL) { // Persistent effect logoutAtom = XInternAtom(display(), "_KDE_LOGGING_OUT", False); @@ -68,6 +74,8 @@ LogoutEffect::~LogoutEffect() { delete blurTexture; delete blurTarget; + delete m_vignettingShader; + delete m_blurShader; } void LogoutEffect::reconfigure(ReconfigureFlags) @@ -80,6 +88,8 @@ void LogoutEffect::reconfigure(ReconfigureFlags) delete blurTarget; blurTarget = NULL; blurSupported = false; + delete m_blurShader; + m_blurShader = NULL; } void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time) @@ -95,7 +105,7 @@ void LogoutEffect::prePaintScreen(ScreenPrePaintData& data, int time) } else if (!blurTexture) { blurSupported = false; delete blurTarget; // catch as we just tested the texture ;-P - if (effects->compositingType() == OpenGLCompositing && GLTexture::NPOTTextureSupported() && useBlur) { + if (effects->compositingType() == OpenGLCompositing && GLTexture::NPOTTextureSupported() && GLRenderTarget::blitSupported() && useBlur) { // TODO: It seems that it is not possible to create a GLRenderTarget that has // a different size than the display right now. Most likely a KWin core bug. // Create texture and render target @@ -180,11 +190,8 @@ void LogoutEffect::paintWindow(EffectWindow* w, int mask, QRegion region, Window void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { - if (blurSupported && progress > 0.0) - GLRenderTarget::pushRenderTarget(blurTarget); effects->paintScreen(mask, region, data); -#ifndef KWIN_HAVE_OPENGLES if (effects->compositingType() == KWin::OpenGLCompositing && progress > 0.0) { if (!blurSupported) { if (!logoutWindowPassed) @@ -193,64 +200,16 @@ void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) // is set as it may still be even if it wasn't rendered. renderVignetting(); } else { - GLRenderTarget* target = GLRenderTarget::popRenderTarget(); - assert(target == blurTarget); - Q_UNUSED(target); + GLRenderTarget::pushRenderTarget(blurTarget); + blurTarget->blitFromFramebuffer(); + GLRenderTarget::popRenderTarget(); //-------------------------- // Render the screen effect - - // HACK: the GL code is still OpenGL 1, so we have to unbind the shader. - GLint shader = 0; - if (ShaderManager::instance()->isShaderBound()) { - glGetIntegerv(GL_CURRENT_PROGRAM, &shader); - glUseProgram(0); - } - glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); - - // Unmodified base image - blurTexture->bind(); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex2f(0.0, displayHeight()); - glTexCoord2f(1.0, 0.0); - glVertex2f(displayWidth(), displayHeight()); - glTexCoord2f(1.0, 1.0); - glVertex2f(displayWidth(), 0.0); - glTexCoord2f(0.0, 1.0); - glVertex2f(0.0, 0.0); - glEnd(); - - // Blurred image - GLfloat bias[1]; - glGetTexEnvfv(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias); - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 1.75); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(1.0f, 1.0f, 1.0f, progress * 0.4); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex2f(0.0, displayHeight()); - glTexCoord2f(1.0, 0.0); - glVertex2f(displayWidth(), displayHeight()); - glTexCoord2f(1.0, 1.0); - glVertex2f(displayWidth(), 0.0); - glTexCoord2f(0.0, 1.0); - glVertex2f(0.0, 0.0); - glEnd(); - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias[0]); - blurTexture->unbind(); + renderBlurTexture(); // Vignetting (Radial gradient with transparent middle and black edges) renderVignetting(); - - glPopAttrib(); - // HACK: rebind previously bound shader - if (ShaderManager::instance()->isShaderBound()) { - glUseProgram(shader); - } - //-------------------------- // Render the logout window @@ -273,7 +232,6 @@ void LogoutEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) windowsOpacities.clear(); } } -#endif } void LogoutEffect::postPaintScreen() @@ -329,6 +287,58 @@ bool LogoutEffect::isLogoutDialog(EffectWindow* w) } void LogoutEffect::renderVignetting() +{ + if (!ShaderManager::instance()->isValid()) { + renderVignettingLegacy(); + return; + } + if (!m_vignettingShader) { + m_vignettingShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::ColorShader, + KGlobal::dirs()->findResource("data", "kwin/vignetting.frag")); + if (!m_vignettingShader->isValid()) { + kDebug(1212) << "Vignetting Shader failed to load"; + return; + } + } else if (!m_vignettingShader->isValid()) { + // shader broken + return; + } + // need to get the projection matrix from the ortho shader for the vignetting shader + QMatrix4x4 projection = ShaderManager::instance()->pushShader(KWin::ShaderManager::SimpleShader)->getUniformMatrix4x4("projection"); + ShaderManager::instance()->popShader(); + + ShaderManager::instance()->pushShader(m_vignettingShader); + m_vignettingShader->setUniform(KWin::GLShader::ProjectionMatrix, projection); + m_vignettingShader->setUniform("u_progress", (float)progress * 0.9f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_SCISSOR_TEST); + const QRect fullArea = effects->clientArea(FullArea, 0, 0); + for (int screen = 0; screen < effects->numScreens(); screen++) { + const QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); + glScissor(screenGeom.x(), displayHeight() - screenGeom.y() - screenGeom.height(), + screenGeom.width(), screenGeom.height()); // GL coords are flipped + const float cenX = screenGeom.x() + screenGeom.width() / 2; + const float cenY = fullArea.height() - screenGeom.y() - screenGeom.height() / 2; + const float r = float((screenGeom.width() > screenGeom.height()) + ? screenGeom.width() : screenGeom.height()) * 0.8f; // Radius + m_vignettingShader->setUniform("u_center", QVector2D(cenX, cenY)); + m_vignettingShader->setUniform("u_radius", r); + QVector vertices; + vertices << screenGeom.x() << screenGeom.y(); + vertices << screenGeom.x() << screenGeom.y() + screenGeom.height(); + vertices << screenGeom.x() + screenGeom.width() << screenGeom.y(); + vertices << screenGeom.x() + screenGeom.width() << screenGeom.y() + screenGeom.height(); + GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); + vbo->setData(vertices.count()/2, 2, vertices.constData(), NULL); + vbo->render(GL_TRIANGLE_STRIP); + } + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + ShaderManager::instance()->popShader(); +} + +void LogoutEffect::renderVignettingLegacy() { #ifndef KWIN_HAVE_OPENGLES glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); @@ -357,6 +367,64 @@ void LogoutEffect::renderVignetting() #endif } +void LogoutEffect::renderBlurTexture() +{ + if (!ShaderManager::instance()->isValid()) { + renderBlurTextureLegacy(); + return; + } + if (!m_blurShader) { + m_blurShader = ShaderManager::instance()->loadFragmentShader(KWin::ShaderManager::SimpleShader, + KGlobal::dirs()->findResource("data", "kwin/logout-blur.frag")); + if (!m_blurShader->isValid()) { + kDebug(1212) << "Logout blur shader failed to load"; + } + } else if (!m_blurShader->isValid()) { + // shader is broken - no need to continue here + return; + } + // Unmodified base image + ShaderManager::instance()->pushShader(m_blurShader); + m_blurShader->setUniform(GLShader::Offset, QVector2D(0, 0)); + m_blurShader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0)); + m_blurShader->setUniform(GLShader::Saturation, 1.0); + m_blurShader->setUniform(GLShader::AlphaToOne, 1); + m_blurShader->setUniform("u_alphaProgress", (float)progress * 0.4f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blurTexture->bind(); + blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight())); + blurTexture->unbind(); + glDisable(GL_BLEND); + ShaderManager::instance()->popShader(); + checkGLError("Render blur texture"); +} + +void LogoutEffect::renderBlurTextureLegacy() +{ +#ifndef KWIN_HAVE_OPENGLES + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); + // Unmodified base image + blurTexture->bind(); + blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight())); + + // Blurred image + GLfloat bias[1]; + glGetTexEnvfv(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias); + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, 1.75); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1.0f, 1.0f, 1.0f, progress * 0.4); + + blurTexture->render(infiniteRegion(), QRect(0, 0, displayWidth(), displayHeight())); + + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, bias[0]); + blurTexture->unbind(); + glPopAttrib(); +#endif +} + void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a) { if (w || a != logoutAtom) @@ -372,11 +440,12 @@ void LogoutEffect::slotPropertyNotify(EffectWindow* w, long a) // We are using a compatible KSMServer therefore only terminate the effect when the // atom is deleted, not when the dialog is closed. canDoPersistent = true; + effects->addRepaintFull(); } bool LogoutEffect::isActive() const { - return progress != 0; + return progress != 0 || logoutWindow; } } // namespace diff --git a/effects/logout/logout.h b/effects/logout/logout.h index 7fb8dc8021..f3f69b21fb 100644 --- a/effects/logout/logout.h +++ b/effects/logout/logout.h @@ -64,6 +64,9 @@ private: EffectWindowList ignoredWindows; void renderVignetting(); + void renderVignettingLegacy(); + void renderBlurTexture(); + void renderBlurTextureLegacy(); int frameDelay; bool blurSupported, useBlur; GLTexture* blurTexture; @@ -71,6 +74,8 @@ private: double windowOpacity; EffectWindowList windows; QHash< EffectWindow*, double > windowsOpacities; + GLShader *m_vignettingShader; + GLShader *m_blurShader; }; } // namespace