From e1d4d50cd65817bfcee473b5609e58b989d2ffc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 14 Nov 2008 16:18:34 +0000 Subject: [PATCH] Some more optimizations to snow effect: * move position updates of flakes to shader * remove the for-loop which updates the flakes. This is handled in the loop for painting the flakes * Don't test in each frame if a flake is on the screen. Precalculate the number of frames when then flake is on the screen and count the flakes. * Set PAINT_SCREEN_TRANSFORMED * Remove all unneeded code (like a QTime object when we already get the time in the method) Now it takes ~ 17 % of CPU usage on my system. Seems to be the maximum of what is possible. svn path=/trunk/KDE/kdebase/workspace/; revision=884305 --- effects/data/snow.vert | 28 +++++++-- effects/snow.cpp | 137 ++++++++++++++++++++++------------------- effects/snow.h | 10 ++- 3 files changed, 104 insertions(+), 71 deletions(-) diff --git a/effects/data/snow.vert b/effects/data/snow.vert index a2b0551960..48cb854335 100644 --- a/effects/data/snow.vert +++ b/effects/data/snow.vert @@ -17,15 +17,31 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -attribute float angle; -attribute float x; -attribute float y; +uniform float angle; +uniform int x; +uniform int width; +uniform int height; +varying float discarding; +uniform int hSpeed; +uniform int vSpeed; +uniform int frames; void main() { - float radian = radians(angle); - mat2 rotation = mat2( cos(radian), sin(radian), -sin(radian), cos(radian) ); - vec2 vertex = (gl_Vertex.xy-vec2(x, y))*rotation+vec2(x, y); gl_TexCoord[0] = gl_MultiTexCoord0; + float radian = radians( angle*float(frames) ); + mat2 rotation = mat2( cos(radian), sin(radian), -sin(radian), cos(radian) ); + float xCoord; + float yCoord; + if( gl_MultiTexCoord0.s == 0.0 ) + xCoord = -float(width)*0.5; + else + xCoord = float(width)*0.5; + if( gl_MultiTexCoord0.t == 0.0 ) + yCoord = -float(height)*0.5; + else + yCoord = float(height)*0.5; + vec2 vertex = vec2( xCoord, yCoord ); + vertex = (vertex)*rotation + vec2( float(x), 0.0 ) + vec2(hSpeed, vSpeed)*float(frames); gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, gl_Vertex.zw); } diff --git a/effects/snow.cpp b/effects/snow.cpp index 68455b5083..e07a3fad6b 100644 --- a/effects/snow.cpp +++ b/effects/snow.cpp @@ -31,8 +31,6 @@ along with this program. If not, see . #include #include -#include -#include #include @@ -47,7 +45,6 @@ KWIN_EFFECT( snow, SnowEffect ) SnowEffect::SnowEffect() : texture( NULL ) - , flakes( NULL ) , active( false) , snowBehindWindows( false ) , mShader( 0 ) @@ -55,7 +52,6 @@ SnowEffect::SnowEffect() , mUseShader( true ) { srandom( std::time( NULL ) ); - lastFlakeTime = QTime::currentTime(); nextFlakeMillis = 0; KActionCollection* actionCollection = new KActionCollection( this ); KAction* a = static_cast< KAction* >( actionCollection->addAction( "Snow" )); @@ -68,8 +64,8 @@ SnowEffect::SnowEffect() SnowEffect::~SnowEffect() { delete texture; - delete flakes; - delete mShader; + if( active ) + delete mShader; } void SnowEffect::reconfigure( ReconfigureFlags ) @@ -87,57 +83,32 @@ void SnowEffect::prePaintScreen( ScreenPrePaintData& data, int time ) { if ( active ) { - if (! flakes ) - { - flakes = new QList(); - lastFlakeTime.start(); - } - int count = flakes->count(); - for (int i=0; ifirst(); - flakes->pop_front(); - // if flake has reached bottom, left or right don't push it back - if ( flake.getY() >= QApplication::desktop()->geometry().bottom() ) - { - continue; - } - else if (flake.getX()+flake.getWidth() <= QApplication::desktop()->geometry().left() ) - { - continue; - } - else if (flake.getX() >= QApplication::desktop()->geometry().right() ) - { - continue; - } - flake.updateSpeedAndRotation(); - flakes->append(flake); - } // if number of active snowflakes is smaller than maximum number // create a random new snowflake - if ( ( lastFlakeTime.elapsed() >= nextFlakeMillis ) && flakes->count() < mNumberFlakes) + nextFlakeMillis -= time; + if ( ( nextFlakeMillis <= 0 ) && flakes.count() < mNumberFlakes) { int size = 0; while ( size < mMinFlakeSize ) size = random() % mMaxFlakeSize; - SnowFlake flake = SnowFlake( random() % (QApplication::desktop()->geometry().right() - size), -1 * size, size, size, mMaxVSpeed, mMaxHSpeed ); - flakes->append( flake ); + SnowFlake flake = SnowFlake( random() % (displayWidth() - size), -1 * size, size, size, mMaxVSpeed, mMaxHSpeed ); + flakes.append( flake ); // calculation of next time of snowflake // depends on the time the flow needs to get to the bottom (screen size) // and the fps long next = ((500/(time+5))*(Effect::displayHeight()/flake.getVSpeed()))/mNumberFlakes; nextFlakeMillis = next; - lastFlakeTime.restart(); } + data.mask |= PAINT_SCREEN_TRANSFORMED; } effects->prePaintScreen( data, time ); } void SnowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) { + if( active && snowBehindWindows ) + repaintRegion = QRegion( 0, 0, displayWidth(), displayHeight() ); effects->paintScreen( mask, region, data ); // paint normal screen if( active && !snowBehindWindows ) snowing( region ); @@ -145,7 +116,6 @@ void SnowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) void SnowEffect::snowing( QRegion& region ) { -#ifdef KWIN_HAVE_OPENGL_COMPOSITING if(! texture ) loadTexture(); if( texture ) { @@ -160,34 +130,33 @@ void SnowEffect::snowing( QRegion& region ) if( mUseShader ) { mShader->bind(); - glNewList( list, GL_COMPILE_AND_EXECUTE ); - glBegin( GL_QUADS ); } else glNewList( list, GL_COMPILE_AND_EXECUTE ); - for (int i=0; icount(); i++) + for (int i=0; iat(i); - if( snowBehindWindows && !region.contains( flake.getRect() ) ) + SnowFlake& flake = flakes[i]; + if( flake.addFrame() == 0 ) + { + flakes.removeAt( i-- ); continue; + } if( mUseShader ) { // use shader - mShader->setAttribute( "angle", flake.getRotationAngle() ); - mShader->setAttribute( "x", flake.getWidth()/2 + flake.getX() ); - mShader->setAttribute( "y", flake.getHeight()/2 + flake.getY() ); - glTexCoord2f( 0.0f, 0.0f ); - glVertex2i( flake.getRect().x(), flake.getRect().y() ); - glTexCoord2f( 1.0f, 0.0f ); - glVertex2i( flake.getRect().x()+flake.getRect().width(), flake.getRect().y() ); - glTexCoord2f( 1.0f, 1.0f ); - glVertex2i( flake.getRect().x()+flake.getRect().width(), flake.getRect().y()+flake.getRect().height() ); - glTexCoord2f( 0.0f, 1.0f ); - glVertex2i( flake.getRect().x(), flake.getRect().y()+flake.getRect().height() ); + mShader->setUniform( "angle", flake.getRotationSpeed() ); + mShader->setUniform( "x", flake.getX() ); + mShader->setUniform( "hSpeed", flake.getHSpeed() ); + mShader->setUniform( "vSpeed", flake.getVSpeed() ); + mShader->setUniform( "frames", flake.getFrames() ); + mShader->setUniform( "width", flake.getWidth() ); + mShader->setUniform( "height", flake.getHeight() ); + glCallList( list ); } else { + flake.updateSpeedAndRotation(); // no shader // save the matrix glPushMatrix(); @@ -210,8 +179,6 @@ void SnowEffect::snowing( QRegion& region ) } if( mUseShader ) { - glEnd(); - glEndList(); mShader->unbind(); } else @@ -220,20 +187,26 @@ void SnowEffect::snowing( QRegion& region ) texture->unbind(); glPopAttrib(); } -#endif } void SnowEffect::postPaintScreen() { if( active ) { - effects->addRepaintFull(); + if( snowBehindWindows ) + effects->addRepaint( repaintRegion ); + else + effects->addRepaintFull(); } effects->postPaintScreen(); } void SnowEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) { + if( active && snowBehindWindows && !w->isDesktop() && !w->isDock() ) + { + repaintRegion -= QRegion( w->geometry() ); + } effects->paintWindow( w, mask, region, data ); if( active && w->isDesktop() && snowBehindWindows ) snowing( region ); @@ -241,6 +214,7 @@ void SnowEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowP void SnowEffect::toggle() { +#ifdef KWIN_HAVE_OPENGL_COMPOSITING active = !active; if( active ) { @@ -249,9 +223,16 @@ void SnowEffect::toggle() else { glDeleteLists( list, 1 ); - flakes->clear(); + flakes.clear(); + if( mUseShader ) + { + delete mShader; + mInited = false; + mUseShader = true; + } } effects->addRepaintFull(); +#endif } bool SnowEffect::loadShader() @@ -284,17 +265,28 @@ bool SnowEffect::loadShader() mShader->unbind(); } kDebug() << "using shader"; + + glNewList( list, GL_COMPILE ); + glBegin( GL_QUADS ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex2i( 0, 0 ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex2i( 0, 0 ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex2i( 0, 0 ); + glTexCoord2f( 0.0f, 1.0f ); + glVertex2i( 0, 0 ); + glEnd(); + glEndList(); return true; } void SnowEffect::loadTexture() { -#ifdef KWIN_HAVE_OPENGL_COMPOSITING QString file = KGlobal::dirs()->findResource( "appdata", "snowflake.png" ); if( file.isEmpty()) return; texture = new GLTexture( file ); -#endif } @@ -312,6 +304,12 @@ SnowFlake::SnowFlake(int x, int y, int width, int height, int maxVSpeed, int max rotationSpeed = random()%4 - 2; if(rotationSpeed == 0) rotationSpeed = 0.5; rect = QRect(x, y, width, height); + frameCounter = 0; + maxFrames = displayHeight() / vSpeed; + if( hSpeed > 0 ) + maxFrames = qMin( maxFrames, (displayWidth() - x)/hSpeed ); + else if( hSpeed < 0 ) + maxFrames = qMin( maxFrames, (x + width)/(-hSpeed) ); } SnowFlake::~SnowFlake() @@ -339,6 +337,11 @@ float SnowFlake::getRotationAngle() return rotationAngle; } +float SnowFlake::getRotationSpeed() + { + return rotationSpeed; + } + int SnowFlake::getVSpeed() { return vSpeed; @@ -384,4 +387,14 @@ void SnowFlake::setY(int y) rect.setY(y); } +int SnowFlake::addFrame() + { + return ( maxFrames - (++frameCounter) ); + } + +int SnowFlake::getFrames() + { + return frameCounter; + } + } // namespace diff --git a/effects/snow.h b/effects/snow.h index f8ecbc7216..4c4ebdc933 100644 --- a/effects/snow.h +++ b/effects/snow.h @@ -28,7 +28,6 @@ along with this program. If not, see . #include #include -#include namespace KWin { @@ -41,11 +40,14 @@ class SnowFlake int getHSpeed(); int getVSpeed(); float getRotationAngle(); + float getRotationSpeed(); void updateSpeedAndRotation(); + int addFrame(); int getHeight(); int getWidth(); int getX(); int getY(); + int getFrames(); QRect getRect(); void setHeight(int height); void setWidth(int width); @@ -56,6 +58,8 @@ class SnowFlake QRect rect; int vSpeed; int hSpeed; + int frameCounter; + int maxFrames; float rotationAngle; float rotationSpeed; }; @@ -80,8 +84,7 @@ class SnowEffect void snowing( QRegion& region ); bool loadShader(); GLTexture* texture; - QList* flakes; - QTime lastFlakeTime; + QList flakes; long nextFlakeMillis; int mNumberFlakes; int mMinFlakeSize; @@ -94,6 +97,7 @@ class SnowEffect GLShader* mShader; bool mInited; bool mUseShader; + QRegion repaintRegion; }; } // namespace