Finally make the bloody blur effect work properly.
This fixes the artefacts appearing when only part of the screen is updated. This version also brings ton of optimizations which might well increase performance 2 or 3 times on slower cards: - Windows are not drawn twice anymore. Now they're drawn only to render target and later changed parts of the render target are copied back onto screen. - Shaders have been optimized. Some calculations moved from pixel shader to vertex shader. - For ARGB windows, if window's opacity is 0 then it will stay transparent instead of being replaced by blurred background. - Blur effect should now play nicer with other effects, e.g. shadows. svn path=/trunk/KDE/kdebase/workspace/; revision=748502
This commit is contained in:
parent
eba221d0a3
commit
213833fc7f
7 changed files with 190 additions and 89 deletions
188
effects/blur.cpp
188
effects/blur.cpp
|
@ -47,12 +47,17 @@ BlurEffect::BlurEffect() : Effect()
|
|||
mWindowShader = 0;
|
||||
|
||||
mBlurRadius = 4;
|
||||
mTime = 0;
|
||||
mValid = loadData();
|
||||
if( !mValid )
|
||||
{
|
||||
kWarning() << "Loading failed";
|
||||
}
|
||||
effects->addRepaintFull();
|
||||
}
|
||||
|
||||
BlurEffect::~BlurEffect()
|
||||
{
|
||||
{
|
||||
effects->addRepaintFull();
|
||||
delete mSceneTexture;
|
||||
delete mTmpTexture;
|
||||
delete mBlurTexture;
|
||||
|
@ -61,7 +66,7 @@ BlurEffect::~BlurEffect()
|
|||
delete mBlurTarget;
|
||||
delete mBlurShader;
|
||||
delete mWindowShader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BlurEffect::loadData()
|
||||
|
@ -140,44 +145,123 @@ bool BlurEffect::supported()
|
|||
(effects->compositingType() == OpenGLCompositing);
|
||||
}
|
||||
|
||||
QRegion BlurEffect::expandedRegion( const QRegion& region ) const
|
||||
{
|
||||
QRegion expandedregion;
|
||||
foreach( QRect r, region.rects() )
|
||||
{
|
||||
r.adjust( -mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius );
|
||||
expandedregion += r;
|
||||
}
|
||||
return expandedregion;
|
||||
}
|
||||
|
||||
void BlurEffect::prePaintScreen( ScreenPrePaintData& data, int time )
|
||||
{
|
||||
mTransparentWindows = 0;
|
||||
mTime += time;
|
||||
mScreenDirty = QRegion();
|
||||
mBlurDirty = QRegion();
|
||||
mBlurMask = QRegion();
|
||||
|
||||
effects->prePaintScreen(data, time);
|
||||
}
|
||||
|
||||
void BlurEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
|
||||
{
|
||||
{
|
||||
// Expand the painted area
|
||||
mBlurMask |= expandedRegion( data.paint );
|
||||
data.paint |= expandedRegion( mBlurMask );
|
||||
effects->prePaintWindow( w, data, time );
|
||||
|
||||
if( w->isPaintingEnabled() && ( data.mask & PAINT_WINDOW_TRANSLUCENT ))
|
||||
mTransparentWindows++;
|
||||
}
|
||||
data.setTranslucent();
|
||||
}
|
||||
|
||||
void BlurEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
|
||||
{
|
||||
if( mValid && mTransparentWindows )
|
||||
void BlurEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
|
||||
{
|
||||
if( mask & PAINT_WINDOW_TRANSLUCENT )
|
||||
// TODO: prePaintWindow() gets called _after_ paintScreen(), so we have no
|
||||
// way of knowing here whether there will be any translucent windows or
|
||||
// not. If we'd know that there's no translucent windows then we could
|
||||
// render straight onto screen, saving some time.
|
||||
if( mValid /*&& mTransparentWindows*/ )
|
||||
{
|
||||
// rendering everything onto render target
|
||||
effects->pushRenderTarget(mSceneTarget);
|
||||
effects->paintScreen( mask, region, data );
|
||||
effects->popRenderTarget();
|
||||
|
||||
// Copy changed areas back onto screen
|
||||
mScreenDirty &= mBlurMask;
|
||||
if( !mScreenDirty.isEmpty() )
|
||||
{
|
||||
if( mask & PAINT_SCREEN_TRANSFORMED )
|
||||
{
|
||||
// We don't want any transformations when working with our own
|
||||
// textures, so load an identity matrix
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
GLTexture* tex = mSceneTexture;
|
||||
int pixels = 0;
|
||||
tex->bind();
|
||||
tex->enableUnnormalizedTexCoords();
|
||||
foreach( QRect r, mScreenDirty.rects() )
|
||||
{
|
||||
r.adjust(0, -1, 0, -1);
|
||||
int rx2 = r.x() + r.width();
|
||||
int ry2 = r.y() + r.height();
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f( r.x(), ry2 ); glVertex2f( r.x(), ry2 );
|
||||
glTexCoord2f( rx2 , ry2 ); glVertex2f( rx2 , ry2 );
|
||||
glTexCoord2f( rx2 , r.y() ); glVertex2f( rx2 , r.y() );
|
||||
glTexCoord2f( r.x(), r.y() ); glVertex2f( r.x(), r.y() );
|
||||
glEnd();
|
||||
pixels += r.width()*r.height();
|
||||
}
|
||||
tex->disableUnnormalizedTexCoords();
|
||||
tex->unbind();
|
||||
|
||||
if( mask & PAINT_SCREEN_TRANSFORMED )
|
||||
{
|
||||
// Restore the original matrix
|
||||
glPopMatrix();
|
||||
}
|
||||
// kDebug() << "Copied" << mScreenDirty.rects().count() << "rects, pixels:" << pixels;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
effects->paintScreen( mask, region, data );
|
||||
}
|
||||
}
|
||||
|
||||
void BlurEffect::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
|
||||
{
|
||||
if( mValid /*&& mTransparentWindows*/ )
|
||||
{
|
||||
if( mask & PAINT_WINDOW_TRANSLUCENT &&
|
||||
(data.opacity != 1.0 || data.contents_opacity != 1.0 || data.decoration_opacity != 1.0 ))
|
||||
{
|
||||
// Make sure the blur texture is up to date
|
||||
if( mask & PAINT_SCREEN_TRANSFORMED )
|
||||
{
|
||||
{
|
||||
// We don't want any transformations when working with our own
|
||||
// textures, so load an identity matrix
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
}
|
||||
}
|
||||
// If we're having transformations, we don't know the window's
|
||||
// transformed position on the screen and thus have to update the
|
||||
// entire screen
|
||||
if( mask & ( PAINT_WINDOW_TRANSFORMED | PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS ) )
|
||||
updateBlurTexture( QRegion(0, 0, displayWidth(), displayHeight()) );
|
||||
else
|
||||
updateBlurTexture(region);
|
||||
updateBlurTexture( mBlurDirty );
|
||||
mBlurDirty = QRegion();
|
||||
if( mask & PAINT_SCREEN_TRANSFORMED )
|
||||
// Restore the original matrix
|
||||
glPopMatrix();
|
||||
|
@ -191,97 +275,87 @@ void BlurEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowP
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// Paint
|
||||
effects->paintWindow( w, mask, region, data );
|
||||
if(mTransparentWindows > 1)
|
||||
{
|
||||
// If we have multiple translucent windows on top of each
|
||||
// other, we need to paint those onto the scene rendertarget
|
||||
// as well
|
||||
effects->pushRenderTarget(mSceneTarget);
|
||||
effects->paintWindow( w, mask, region, data );
|
||||
effects->popRenderTarget();
|
||||
}
|
||||
effects->drawWindow( w, mask, region, data );
|
||||
|
||||
// Disable blur texture and shader
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
mBlurTexture->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
mWindowShader->unbind();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
// Opaque window
|
||||
// Paint to the screen...
|
||||
effects->paintWindow( w, mask, region, data );
|
||||
// ...and to the rendertarget as well
|
||||
effects->pushRenderTarget(mSceneTarget);
|
||||
effects->paintWindow( w, mask, region, data );
|
||||
effects->popRenderTarget();
|
||||
// Paint to the rendertarget (which is already being used)
|
||||
effects->drawWindow( w, mask, region, data );
|
||||
}
|
||||
// Mark the window's region as dirty
|
||||
mScreenDirty += region;
|
||||
mBlurDirty += region & mBlurMask;
|
||||
}
|
||||
}
|
||||
else
|
||||
// If there are no translucent windows then paint as usual
|
||||
effects->paintWindow( w, mask, region, data );
|
||||
}
|
||||
effects->drawWindow( w, mask, region, data );
|
||||
}
|
||||
|
||||
void BlurEffect::updateBlurTexture(const QRegion& region)
|
||||
{
|
||||
{
|
||||
QRect bounding = region.boundingRect();
|
||||
QVector<QRect> rects = region.rects();
|
||||
int totalarea = 0;
|
||||
foreach( QRect r, rects )
|
||||
totalarea += r.width() * r.height();
|
||||
if( (int)(totalarea * 1.33 + 100 ) < bounding.width() * bounding.height() )
|
||||
{
|
||||
{
|
||||
// Use small rects
|
||||
updateBlurTexture(rects);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
// Bounding rect is probably cheaper
|
||||
QVector<QRect> tmp( 1, bounding );
|
||||
updateBlurTexture( tmp );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlurEffect::updateBlurTexture(const QVector<QRect>& rects)
|
||||
{
|
||||
{
|
||||
// Blur
|
||||
// First pass (vertical)
|
||||
effects->pushRenderTarget(mTmpTarget);
|
||||
mBlurShader->bind();
|
||||
mSceneTexture->bind();
|
||||
effects->pushRenderTarget(mTmpTarget);
|
||||
mBlurShader->setAttribute("xBlur", 0.0f);
|
||||
mBlurShader->setAttribute("yBlur", 1.0f);
|
||||
|
||||
mBlurShader->setAttribute("xBlur", 0);
|
||||
mBlurShader->setAttribute("yBlur", 1);
|
||||
mSceneTexture->bind();
|
||||
|
||||
foreach( QRect r, rects )
|
||||
{
|
||||
r.adjust(-mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius);
|
||||
// We change x coordinates here because horizontal blur pass (which
|
||||
// comes after this one) also uses pixels that are horizontally edging
|
||||
// the blurred area. Thus we need to make sure that those pixels are
|
||||
// also updated.
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f( r.x() , r.y() + r.height() );
|
||||
glVertex2f( r.x() + r.width(), r.y() + r.height() );
|
||||
glVertex2f( r.x() + r.width(), r.y() );
|
||||
glVertex2f( r.x() , r.y() );
|
||||
glVertex2f( r.x()-mBlurRadius , r.y() + r.height() );
|
||||
glVertex2f( r.x() + r.width()+mBlurRadius, r.y() + r.height() );
|
||||
glVertex2f( r.x() + r.width()+mBlurRadius, r.y() );
|
||||
glVertex2f( r.x()-mBlurRadius , r.y() );
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
mSceneTexture->unbind();
|
||||
mBlurShader->unbind();
|
||||
effects->popRenderTarget();
|
||||
|
||||
// Second pass (horizontal)
|
||||
effects->pushRenderTarget(mBlurTarget);
|
||||
mBlurShader->bind();
|
||||
mTmpTexture->bind();
|
||||
mBlurShader->setAttribute("xBlur", 1.0f);
|
||||
mBlurShader->setAttribute("yBlur", 0.0f);
|
||||
|
||||
mBlurShader->setAttribute("xBlur", 1);
|
||||
mBlurShader->setAttribute("yBlur", 0);
|
||||
mTmpTexture->bind();
|
||||
|
||||
foreach( QRect r, rects )
|
||||
{
|
||||
r.adjust(-mBlurRadius, -mBlurRadius, mBlurRadius, mBlurRadius);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f( r.x() , r.y() + r.height() );
|
||||
glVertex2f( r.x() + r.width(), r.y() + r.height() );
|
||||
|
@ -292,9 +366,9 @@ void BlurEffect::updateBlurTexture(const QVector<QRect>& rects)
|
|||
|
||||
|
||||
mTmpTexture->unbind();
|
||||
mBlurShader->unbind();
|
||||
effects->popRenderTarget();
|
||||
}
|
||||
mBlurShader->unbind();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
|
@ -75,9 +75,10 @@ X-KDE-ServiceTypes=KWin/Effect
|
|||
X-KDE-PluginInfo-Author=Rivo Laks
|
||||
X-KDE-PluginInfo-Email=rivolaks@hot.ee
|
||||
X-KDE-PluginInfo-Name=kwin4_effect_blur
|
||||
X-KDE-PluginInfo-Version=0.1.0
|
||||
X-KDE-PluginInfo-Version=0.2.0
|
||||
X-KDE-PluginInfo-Category=Appearance
|
||||
X-KDE-PluginInfo-Depends=
|
||||
X-KDE-PluginInfo-License=GPL
|
||||
X-KDE-PluginInfo-EnabledByDefault=false
|
||||
X-KDE-Library=kwin4_effect_builtins
|
||||
X-Ordering=85
|
||||
|
|
|
@ -24,6 +24,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
// Include with base class for effects.
|
||||
#include <kwineffects.h>
|
||||
|
||||
#include <QRegion>
|
||||
|
||||
template< class T > class QVector;
|
||||
|
||||
|
||||
|
@ -44,9 +46,10 @@ class BlurEffect : public Effect
|
|||
~BlurEffect();
|
||||
|
||||
virtual void prePaintScreen( ScreenPrePaintData& data, int time );
|
||||
virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data );
|
||||
|
||||
virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time );
|
||||
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
|
||||
virtual void drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
|
||||
|
||||
static bool supported();
|
||||
|
||||
|
@ -56,6 +59,8 @@ class BlurEffect : public Effect
|
|||
void updateBlurTexture(const QVector<QRect>& rects);
|
||||
void updateBlurTexture(const QRegion& region);
|
||||
|
||||
QRegion expandedRegion( const QRegion& r ) const;
|
||||
|
||||
private:
|
||||
GLTexture* mSceneTexture;
|
||||
GLTexture* mTmpTexture;
|
||||
|
@ -69,7 +74,11 @@ class BlurEffect : public Effect
|
|||
int mBlurRadius;
|
||||
|
||||
int mTransparentWindows;
|
||||
int mTime;
|
||||
|
||||
QRegion mBlurDirty;
|
||||
QRegion mScreenDirty;
|
||||
|
||||
QRegion mBlurMask;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -14,7 +14,7 @@ vec2 pix2tex(vec2 pix)
|
|||
}
|
||||
|
||||
// Returns color of the window at given texture coordinate, taking into
|
||||
// account opacity, brightness and saturation
|
||||
// account brightness and saturation, but not opacity
|
||||
vec4 windowColor(vec2 texcoord)
|
||||
{
|
||||
vec4 color = texture2D(windowTex, texcoord);
|
||||
|
@ -23,21 +23,18 @@ vec4 windowColor(vec2 texcoord)
|
|||
color.rgb = mix(vec3(grayscale), color.rgb, saturation);
|
||||
// Apply brightness
|
||||
color.rgb = color.rgb * brightness;
|
||||
// Apply opacity
|
||||
color.a = color.a * opacity;
|
||||
// and return
|
||||
return color;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texcoord = (gl_TexCoord[0] * gl_TextureMatrix[0]).xy;
|
||||
vec2 blurtexcoord = pix2tex(gl_FragCoord.xy); //(gl_FragCoord * gl_TextureMatrix[4]).xy;
|
||||
vec2 blurtexcoord = pix2tex(gl_FragCoord.xy);
|
||||
|
||||
vec4 winColor = windowColor(texcoord);
|
||||
vec4 winColor = windowColor(gl_TexCoord[0].xy);
|
||||
vec3 tex = mix(texture2D(backgroundTex, blurtexcoord).rgb,
|
||||
winColor.rgb, winColor.a * opacity);
|
||||
|
||||
gl_FragColor = vec4(tex, 1.0);
|
||||
gl_FragColor = vec4(tex, pow(winColor.a, 0.2));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
void main()
|
||||
{
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0 * gl_TextureMatrix[0];
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
uniform sampler2D inputTex;
|
||||
uniform float textureWidth;
|
||||
uniform float textureHeight;
|
||||
|
||||
varying vec2 pos;
|
||||
varying vec2 blurDirection;
|
||||
varying vec2 samplePos1;
|
||||
varying vec2 samplePos2;
|
||||
varying vec2 samplePos3;
|
||||
varying vec2 samplePos4;
|
||||
varying vec2 samplePos5;
|
||||
|
||||
|
||||
// Converts pixel coordinates to texture coordinates
|
||||
vec2 pix2tex(vec2 pix)
|
||||
// If defined, use five samples (blur radius = 5), otherwise 3 samples (radius = 3)
|
||||
#define FIVE_SAMPLES
|
||||
|
||||
|
||||
vec3 blurTex(vec2 pos, float strength)
|
||||
{
|
||||
return vec2(pix.x / textureWidth, 1.0 - pix.y / textureHeight);
|
||||
}
|
||||
|
||||
vec3 blurTex(float offset, float strength)
|
||||
{
|
||||
return texture2D(inputTex, pix2tex(pos + blurDirection * offset)).rgb * strength;
|
||||
return texture2D(inputTex, pos).rgb * strength;
|
||||
}
|
||||
|
||||
void main()
|
||||
|
@ -23,11 +22,17 @@ void main()
|
|||
// This blur actually has a radius of 4, but we take advantage of gpu's
|
||||
// linear texture filtering, so e.g. 1.5 actually gives us both texels
|
||||
// 1 and 2
|
||||
vec3 tex = blurTex(0.0, 0.20);
|
||||
tex += blurTex(-1.5, 0.30);
|
||||
tex += blurTex( 1.5, 0.30);
|
||||
tex += blurTex(-3.5, 0.10);
|
||||
tex += blurTex( 3.5, 0.10);
|
||||
#ifdef FIVE_SAMPLES
|
||||
vec3 tex = blurTex(samplePos1, 0.30);
|
||||
tex += blurTex(samplePos2, 0.25);
|
||||
tex += blurTex(samplePos3, 0.25);
|
||||
tex += blurTex(samplePos4, 0.1);
|
||||
tex += blurTex(samplePos5, 0.1);
|
||||
#else
|
||||
vec3 tex = blurTex(samplePos1, 0.40);
|
||||
tex += blurTex(samplePos2, 0.30);
|
||||
tex += blurTex(samplePos3, 0.30);
|
||||
#endif
|
||||
|
||||
gl_FragColor = vec4(tex, 1.0);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
varying vec2 pos;
|
||||
varying vec2 blurDirection;
|
||||
varying vec2 samplePos1;
|
||||
varying vec2 samplePos2;
|
||||
varying vec2 samplePos3;
|
||||
varying vec2 samplePos4;
|
||||
varying vec2 samplePos5;
|
||||
|
||||
uniform float textureWidth;
|
||||
uniform float textureHeight;
|
||||
attribute float xBlur;
|
||||
attribute float yBlur;
|
||||
|
||||
|
||||
vec2 mkSamplePos(vec2 origin, float offset)
|
||||
{
|
||||
vec2 foo = origin + vec2(xBlur, yBlur) * offset;
|
||||
return vec2(foo.x / textureWidth, 1.0 - foo.y / textureHeight);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
blurDirection = vec2(xBlur, yBlur);
|
||||
pos = gl_Vertex.xy;
|
||||
samplePos1 = mkSamplePos(gl_Vertex.xy, 0.0);
|
||||
samplePos2 = mkSamplePos(gl_Vertex.xy, -1.5);
|
||||
samplePos3 = mkSamplePos(gl_Vertex.xy, 1.5);
|
||||
samplePos4 = mkSamplePos(gl_Vertex.xy, 3.5);
|
||||
samplePos5 = mkSamplePos(gl_Vertex.xy, -3.5);
|
||||
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue