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 You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/ *********************************************************************/
attribute float angle; uniform float angle;
attribute float x; uniform int x;
attribute float y; uniform int width;
uniform int height;
varying float discarding;
uniform int hSpeed;
uniform int vSpeed;
uniform int frames;
void main() 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; 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); 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 <kstandarddirs.h>
#include <kdebug.h> #include <kdebug.h>
#include <QApplication>
#include <QDesktopWidget>
#include <ctime> #include <ctime>
@ -47,7 +45,6 @@ KWIN_EFFECT( snow, SnowEffect )
SnowEffect::SnowEffect() SnowEffect::SnowEffect()
: texture( NULL ) : texture( NULL )
, flakes( NULL )
, active( false) , active( false)
, snowBehindWindows( false ) , snowBehindWindows( false )
, mShader( 0 ) , mShader( 0 )
@ -55,7 +52,6 @@ SnowEffect::SnowEffect()
, mUseShader( true ) , mUseShader( true )
{ {
srandom( std::time( NULL ) ); srandom( std::time( NULL ) );
lastFlakeTime = QTime::currentTime();
nextFlakeMillis = 0; nextFlakeMillis = 0;
KActionCollection* actionCollection = new KActionCollection( this ); KActionCollection* actionCollection = new KActionCollection( this );
KAction* a = static_cast< KAction* >( actionCollection->addAction( "Snow" )); KAction* a = static_cast< KAction* >( actionCollection->addAction( "Snow" ));
@ -68,8 +64,8 @@ SnowEffect::SnowEffect()
SnowEffect::~SnowEffect() SnowEffect::~SnowEffect()
{ {
delete texture; delete texture;
delete flakes; if( active )
delete mShader; delete mShader;
} }
void SnowEffect::reconfigure( ReconfigureFlags ) void SnowEffect::reconfigure( ReconfigureFlags )
@ -87,57 +83,32 @@ void SnowEffect::prePaintScreen( ScreenPrePaintData& data, int time )
{ {
if ( active ) 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 // if number of active snowflakes is smaller than maximum number
// create a random new snowflake // create a random new snowflake
if ( ( lastFlakeTime.elapsed() >= nextFlakeMillis ) && flakes->count() < mNumberFlakes) nextFlakeMillis -= time;
if ( ( nextFlakeMillis <= 0 ) && flakes.count() < mNumberFlakes)
{ {
int size = 0; int size = 0;
while ( size < mMinFlakeSize ) while ( size < mMinFlakeSize )
size = random() % mMaxFlakeSize; size = random() % mMaxFlakeSize;
SnowFlake flake = SnowFlake( random() % (QApplication::desktop()->geometry().right() - size), -1 * size, size, size, mMaxVSpeed, mMaxHSpeed ); SnowFlake flake = SnowFlake( random() % (displayWidth() - size), -1 * size, size, size, mMaxVSpeed, mMaxHSpeed );
flakes->append( flake ); flakes.append( flake );
// calculation of next time of snowflake // calculation of next time of snowflake
// depends on the time the flow needs to get to the bottom (screen size) // depends on the time the flow needs to get to the bottom (screen size)
// and the fps // and the fps
long next = ((500/(time+5))*(Effect::displayHeight()/flake.getVSpeed()))/mNumberFlakes; long next = ((500/(time+5))*(Effect::displayHeight()/flake.getVSpeed()))/mNumberFlakes;
nextFlakeMillis = next; nextFlakeMillis = next;
lastFlakeTime.restart();
} }
data.mask |= PAINT_SCREEN_TRANSFORMED;
} }
effects->prePaintScreen( data, time ); effects->prePaintScreen( data, time );
} }
void SnowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) 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 effects->paintScreen( mask, region, data ); // paint normal screen
if( active && !snowBehindWindows ) if( active && !snowBehindWindows )
snowing( region ); snowing( region );
@ -145,7 +116,6 @@ void SnowEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
void SnowEffect::snowing( QRegion& region ) void SnowEffect::snowing( QRegion& region )
{ {
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
if(! texture ) loadTexture(); if(! texture ) loadTexture();
if( texture ) if( texture )
{ {
@ -160,34 +130,33 @@ void SnowEffect::snowing( QRegion& region )
if( mUseShader ) if( mUseShader )
{ {
mShader->bind(); mShader->bind();
glNewList( list, GL_COMPILE_AND_EXECUTE );
glBegin( GL_QUADS );
} }
else else
glNewList( list, GL_COMPILE_AND_EXECUTE ); 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); SnowFlake& flake = flakes[i];
if( snowBehindWindows && !region.contains( flake.getRect() ) ) if( flake.addFrame() == 0 )
{
flakes.removeAt( i-- );
continue; continue;
}
if( mUseShader ) if( mUseShader )
{ {
// use shader // use shader
mShader->setAttribute( "angle", flake.getRotationAngle() ); mShader->setUniform( "angle", flake.getRotationSpeed() );
mShader->setAttribute( "x", flake.getWidth()/2 + flake.getX() ); mShader->setUniform( "x", flake.getX() );
mShader->setAttribute( "y", flake.getHeight()/2 + flake.getY() ); mShader->setUniform( "hSpeed", flake.getHSpeed() );
glTexCoord2f( 0.0f, 0.0f ); mShader->setUniform( "vSpeed", flake.getVSpeed() );
glVertex2i( flake.getRect().x(), flake.getRect().y() ); mShader->setUniform( "frames", flake.getFrames() );
glTexCoord2f( 1.0f, 0.0f ); mShader->setUniform( "width", flake.getWidth() );
glVertex2i( flake.getRect().x()+flake.getRect().width(), flake.getRect().y() ); mShader->setUniform( "height", flake.getHeight() );
glTexCoord2f( 1.0f, 1.0f ); glCallList( list );
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() );
} }
else else
{ {
flake.updateSpeedAndRotation();
// no shader // no shader
// save the matrix // save the matrix
glPushMatrix(); glPushMatrix();
@ -210,8 +179,6 @@ void SnowEffect::snowing( QRegion& region )
} }
if( mUseShader ) if( mUseShader )
{ {
glEnd();
glEndList();
mShader->unbind(); mShader->unbind();
} }
else else
@ -220,20 +187,26 @@ void SnowEffect::snowing( QRegion& region )
texture->unbind(); texture->unbind();
glPopAttrib(); glPopAttrib();
} }
#endif
} }
void SnowEffect::postPaintScreen() void SnowEffect::postPaintScreen()
{ {
if( active ) if( active )
{ {
effects->addRepaintFull(); if( snowBehindWindows )
effects->addRepaint( repaintRegion );
else
effects->addRepaintFull();
} }
effects->postPaintScreen(); effects->postPaintScreen();
} }
void SnowEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) 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 ); effects->paintWindow( w, mask, region, data );
if( active && w->isDesktop() && snowBehindWindows ) if( active && w->isDesktop() && snowBehindWindows )
snowing( region ); snowing( region );
@ -241,6 +214,7 @@ void SnowEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowP
void SnowEffect::toggle() void SnowEffect::toggle()
{ {
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
active = !active; active = !active;
if( active ) if( active )
{ {
@ -249,9 +223,16 @@ void SnowEffect::toggle()
else else
{ {
glDeleteLists( list, 1 ); glDeleteLists( list, 1 );
flakes->clear(); flakes.clear();
if( mUseShader )
{
delete mShader;
mInited = false;
mUseShader = true;
}
} }
effects->addRepaintFull(); effects->addRepaintFull();
#endif
} }
bool SnowEffect::loadShader() bool SnowEffect::loadShader()
@ -284,17 +265,28 @@ bool SnowEffect::loadShader()
mShader->unbind(); mShader->unbind();
} }
kDebug() << "using shader"; 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; return true;
} }
void SnowEffect::loadTexture() void SnowEffect::loadTexture()
{ {
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
QString file = KGlobal::dirs()->findResource( "appdata", "snowflake.png" ); QString file = KGlobal::dirs()->findResource( "appdata", "snowflake.png" );
if( file.isEmpty()) if( file.isEmpty())
return; return;
texture = new GLTexture( file ); 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; rotationSpeed = random()%4 - 2;
if(rotationSpeed == 0) rotationSpeed = 0.5; if(rotationSpeed == 0) rotationSpeed = 0.5;
rect = QRect(x, y, width, height); 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() SnowFlake::~SnowFlake()
@ -339,6 +337,11 @@ float SnowFlake::getRotationAngle()
return rotationAngle; return rotationAngle;
} }
float SnowFlake::getRotationSpeed()
{
return rotationSpeed;
}
int SnowFlake::getVSpeed() int SnowFlake::getVSpeed()
{ {
return vSpeed; return vSpeed;
@ -384,4 +387,14 @@ void SnowFlake::setY(int y)
rect.setY(y); rect.setY(y);
} }
int SnowFlake::addFrame()
{
return ( maxFrames - (++frameCounter) );
}
int SnowFlake::getFrames()
{
return frameCounter;
}
} // namespace } // namespace

View file

@ -28,7 +28,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <qobject.h> #include <qobject.h>
#include <QList> #include <QList>
#include <QTime>
namespace KWin namespace KWin
{ {
@ -41,11 +40,14 @@ class SnowFlake
int getHSpeed(); int getHSpeed();
int getVSpeed(); int getVSpeed();
float getRotationAngle(); float getRotationAngle();
float getRotationSpeed();
void updateSpeedAndRotation(); void updateSpeedAndRotation();
int addFrame();
int getHeight(); int getHeight();
int getWidth(); int getWidth();
int getX(); int getX();
int getY(); int getY();
int getFrames();
QRect getRect(); QRect getRect();
void setHeight(int height); void setHeight(int height);
void setWidth(int width); void setWidth(int width);
@ -56,6 +58,8 @@ class SnowFlake
QRect rect; QRect rect;
int vSpeed; int vSpeed;
int hSpeed; int hSpeed;
int frameCounter;
int maxFrames;
float rotationAngle; float rotationAngle;
float rotationSpeed; float rotationSpeed;
}; };
@ -80,8 +84,7 @@ class SnowEffect
void snowing( QRegion& region ); void snowing( QRegion& region );
bool loadShader(); bool loadShader();
GLTexture* texture; GLTexture* texture;
QList<SnowFlake>* flakes; QList<SnowFlake> flakes;
QTime lastFlakeTime;
long nextFlakeMillis; long nextFlakeMillis;
int mNumberFlakes; int mNumberFlakes;
int mMinFlakeSize; int mMinFlakeSize;
@ -94,6 +97,7 @@ class SnowEffect
GLShader* mShader; GLShader* mShader;
bool mInited; bool mInited;
bool mUseShader; bool mUseShader;
QRegion repaintRegion;
}; };
} // namespace } // namespace