Commit 2697a273 authored by Martin Flöser's avatar Martin Flöser
Browse files

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
parent 2dadb54a
......@@ -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
......
......@@ -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 )
uniform sampler2D sampler;
uniform float u_alphaProgress;
varying vec2 varyingTexCoords;
void main() {
gl_FragColor = texture2D(sampler, varyingTexCoords, 1.75);
gl_FragColor.a = u_alphaProgress;
}
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);
}
......@@ -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
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment