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
icc-effect-5.14.5
Martin Gräßlin 2012-07-09 19:04:56 +02:00
parent 825a9c4f6e
commit 3125333f76
6 changed files with 155 additions and 61 deletions

View File

@ -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

View File

@ -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 )

View File

@ -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;
}

View File

@ -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);
}

View File

@ -27,6 +27,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <math.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <KDE/KStandardDirs>
#include <QtGui/QMatrix4x4>
#include <QtGui/QVector2D>
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<float> 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

View File

@ -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