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:
parent
c00589e6d7
commit
e1d4d50cd6
3 changed files with 104 additions and 71 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
137
effects/snow.cpp
137
effects/snow.cpp
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue