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
This commit is contained in:
Martin Gräßlin 2008-11-14 16:18:34 +00:00
parent c00589e6d7
commit e1d4d50cd6
3 changed files with 104 additions and 71 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*********************************************************************/
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);
}

View file

@ -31,8 +31,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kstandarddirs.h>
#include <kdebug.h>
#include <QApplication>
#include <QDesktopWidget>
#include <ctime>
@ -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<SnowFlake>();
lastFlakeTime.start();
}
int count = flakes->count();
for (int i=0; i<count; i++)
{
// move flake to bottom. Therefore pop the flake, change x and y and push
// flake back to QVector
SnowFlake flake = flakes->first();
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; i<flakes->count(); i++)
for (int i=0; i<flakes.count(); i++)
{
SnowFlake flake = flakes->at(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

View file

@ -28,7 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <qobject.h>
#include <QList>
#include <QTime>
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<SnowFlake>* flakes;
QTime lastFlakeTime;
QList<SnowFlake> flakes;
long nextFlakeMillis;
int mNumberFlakes;
int mMinFlakeSize;
@ -94,6 +97,7 @@ class SnowEffect
GLShader* mShader;
bool mInited;
bool mUseShader;
QRegion repaintRegion;
};
} // namespace